{
  "config": {
    "configFile": "/var/www/360lm/playwright.config.js",
    "rootDir": "/var/www/360lm/tests",
    "forbidOnly": false,
    "fullyParallel": false,
    "globalSetup": null,
    "globalTeardown": null,
    "globalTimeout": 0,
    "grep": {},
    "grepInvert": null,
    "maxFailures": 0,
    "metadata": {
      "actualWorkers": 1
    },
    "preserveOutput": "always",
    "projects": [
      {
        "outputDir": "/var/www/360lm/test-results",
        "repeatEach": 1,
        "retries": 1,
        "metadata": {
          "actualWorkers": 1
        },
        "id": "android-chrome",
        "name": "android-chrome",
        "testDir": "/var/www/360lm/tests",
        "testIgnore": [],
        "testMatch": [
          "**/*.@(spec|test).?(c|m)[jt]s?(x)"
        ],
        "timeout": 30000
      },
      {
        "outputDir": "/var/www/360lm/test-results",
        "repeatEach": 1,
        "retries": 1,
        "metadata": {
          "actualWorkers": 1
        },
        "id": "desktop-chrome",
        "name": "desktop-chrome",
        "testDir": "/var/www/360lm/tests",
        "testIgnore": [],
        "testMatch": [
          "**/*.@(spec|test).?(c|m)[jt]s?(x)"
        ],
        "timeout": 30000
      }
    ],
    "quiet": false,
    "reporter": [
      [
        "json"
      ]
    ],
    "reportSlowTests": {
      "max": 5,
      "threshold": 300000
    },
    "shard": null,
    "tags": [],
    "updateSnapshots": "missing",
    "updateSourceMethod": "patch",
    "version": "1.60.0",
    "workers": 1,
    "webServer": null
  },
  "suites": [
    {
      "title": "activity.spec.js",
      "file": "activity.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Activity — Smoke",
          "file": "activity.spec.js",
          "line": 10,
          "column": 6,
          "specs": [
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1187,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:26.247Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-45bb9da2781273921e33",
              "file": "activity.spec.js",
              "line": 11,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 205,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:27.610Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-d6284674306e1787a594",
              "file": "activity.spec.js",
              "line": 18,
              "column": 3
            },
            {
              "title": "sw.js contains current version",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 219,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:27.850Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-bea7455145b8c2c4d860",
              "file": "activity.spec.js",
              "line": 23,
              "column": 3
            },
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1194,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:08.177Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-0f05c1db7f936b1770cf",
              "file": "activity.spec.js",
              "line": 11,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 196,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:09.581Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-2112e78eca790609920b",
              "file": "activity.spec.js",
              "line": 18,
              "column": 3
            },
            {
              "title": "sw.js contains current version",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 203,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:09.803Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-a830c832290db1660126",
              "file": "activity.spec.js",
              "line": 23,
              "column": 3
            }
          ]
        },
        {
          "title": "Activity — HTML Structure (core)",
          "file": "activity.spec.js",
          "line": 31,
          "column": 6,
          "specs": [
            {
              "title": "capture screen and submit/draft buttons present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 85,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.098Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-77f84d04d074f2d70a94",
              "file": "activity.spec.js",
              "line": 32,
              "column": 3
            },
            {
              "title": "response list and presentation screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 26,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.199Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-3c3440e71d456b4fbe8a",
              "file": "activity.spec.js",
              "line": 47,
              "column": 3
            },
            {
              "title": "capture screen and submit/draft buttons present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 60,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.025Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-7610f56fa1f0287f3249",
              "file": "activity.spec.js",
              "line": 32,
              "column": 3
            },
            {
              "title": "response list and presentation screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 22,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.099Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-a28fb01019a5d12e16a6",
              "file": "activity.spec.js",
              "line": 47,
              "column": 3
            }
          ]
        },
        {
          "title": "Activity — HTML Structure (v21 draft datetime)",
          "file": "activity.spec.js",
          "line": 62,
          "column": 6,
          "specs": [
            {
              "title": "submit-datetime modal elements present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 30,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.240Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-16370f92dcc0d957eeb0",
              "file": "activity.spec.js",
              "line": 63,
              "column": 3
            },
            {
              "title": "IST datetime helper functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 15,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.285Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-7fc08d7e1d426a968e27",
              "file": "activity.spec.js",
              "line": 78,
              "column": 3
            },
            {
              "title": "submission datetime picker flow functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.317Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-ae0062346cb3397cc1f4",
              "file": "activity.spec.js",
              "line": 91,
              "column": 3
            },
            {
              "title": "audit trail CSS and audit panel in buildPresentation present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.346Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-c3260fe8ff436c5cd715",
              "file": "activity.spec.js",
              "line": 105,
              "column": 3
            },
            {
              "title": "draft PATCH includes submitted_at and error guard",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.375Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-c63db10b1de7f63715bf",
              "file": "activity.spec.js",
              "line": 121,
              "column": 3
            },
            {
              "title": "submit-datetime modal elements present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.132Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-3204473521eed1129c8c",
              "file": "activity.spec.js",
              "line": 63,
              "column": 3
            },
            {
              "title": "IST datetime helper functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.159Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-a7c7cb170591b9ae6d16",
              "file": "activity.spec.js",
              "line": 78,
              "column": 3
            },
            {
              "title": "submission datetime picker flow functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 14,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.183Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-124f71d700a952519bbd",
              "file": "activity.spec.js",
              "line": 91,
              "column": 3
            },
            {
              "title": "audit trail CSS and audit panel in buildPresentation present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 12,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.208Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-aadb3c0f20eca04815f5",
              "file": "activity.spec.js",
              "line": 105,
              "column": 3
            },
            {
              "title": "draft PATCH includes submitted_at and error guard",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 10,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.231Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-7c8b03df43309d0e3ef7",
              "file": "activity.spec.js",
              "line": 121,
              "column": 3
            }
          ]
        },
        {
          "title": "Activity — HTML Structure (v23 cascading filters)",
          "file": "activity.spec.js",
          "line": 134,
          "column": 6,
          "specs": [
            {
              "title": "audit trail check uses ADMIN_IDS not isAdmin",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.399Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-05066d6a9f247b0e561b",
              "file": "activity.spec.js",
              "line": 135,
              "column": 3
            },
            {
              "title": "draft-btn text reset in buildCaptureForm",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.425Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-22f8c98b04c7f4d09112",
              "file": "activity.spec.js",
              "line": 146,
              "column": 3
            },
            {
              "title": "client-side min-date validation in confirmSubmitDatetime",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 9,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.449Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-baba157449c608af4acd",
              "file": "activity.spec.js",
              "line": 156,
              "column": 3
            },
            {
              "title": "server error message extracted from PostgREST JSON",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 11,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.467Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-83c8b0c6216f28424f1f",
              "file": "activity.spec.js",
              "line": 167,
              "column": 3
            },
            {
              "title": "hint text uses human-readable format",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.489Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-5b7638844423a2799628",
              "file": "activity.spec.js",
              "line": 177,
              "column": 3
            },
            {
              "title": "cascading filter — computeAvailable and matchesFiltersExcept present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 10,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.519Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-d98e836565a5867d0c7e",
              "file": "activity.spec.js",
              "line": 188,
              "column": 3
            },
            {
              "title": "cascading filter — status split into Lead Type + Submission rows",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 12,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.538Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-dccccd0142bf3cf29769",
              "file": "activity.spec.js",
              "line": 199,
              "column": 3
            },
            {
              "title": "cascading filter — sequence badge CSS present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.561Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-5c6d3c53b52be256b4c1",
              "file": "activity.spec.js",
              "line": 213,
              "column": 3
            },
            {
              "title": "cascading filter — stats panel is filter-aware",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 14,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.588Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-fd3da6eb4749870ff2aa",
              "file": "activity.spec.js",
              "line": 225,
              "column": 3
            },
            {
              "title": "audit trail check uses ADMIN_IDS not isAdmin",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 9,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.251Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-c8befe1e0a8dda0871e7",
              "file": "activity.spec.js",
              "line": 135,
              "column": 3
            },
            {
              "title": "draft-btn text reset in buildCaptureForm",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 11,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.269Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-906574b62a27c3eb7f7a",
              "file": "activity.spec.js",
              "line": 146,
              "column": 3
            },
            {
              "title": "client-side min-date validation in confirmSubmitDatetime",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 10,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.291Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-c7b6ed3fd63d74e0b6ae",
              "file": "activity.spec.js",
              "line": 156,
              "column": 3
            },
            {
              "title": "server error message extracted from PostgREST JSON",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 8,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.310Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-883777665f5cc6cca6a0",
              "file": "activity.spec.js",
              "line": 167,
              "column": 3
            },
            {
              "title": "hint text uses human-readable format",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 9,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.328Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-b3520d121514076d339a",
              "file": "activity.spec.js",
              "line": 177,
              "column": 3
            },
            {
              "title": "cascading filter — computeAvailable and matchesFiltersExcept present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 12,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.346Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-b67c6919a96c2ec3c1a9",
              "file": "activity.spec.js",
              "line": 188,
              "column": 3
            },
            {
              "title": "cascading filter — status split into Lead Type + Submission rows",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.371Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-5064cb61a86a4e40bf45",
              "file": "activity.spec.js",
              "line": 199,
              "column": 3
            },
            {
              "title": "cascading filter — sequence badge CSS present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 11,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.400Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-e6d9f0118ec03ce3639d",
              "file": "activity.spec.js",
              "line": 213,
              "column": 3
            },
            {
              "title": "cascading filter — stats panel is filter-aware",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.421Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-d74f8e534587325d728b",
              "file": "activity.spec.js",
              "line": 225,
              "column": 3
            }
          ]
        },
        {
          "title": "Activity — Hub Navigation",
          "file": "activity.spec.js",
          "line": 238,
          "column": 6,
          "specs": [
            {
              "title": "home topbar has Hub button",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 0,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 35,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n    at /var/www/360lm/tests/activity.spec.js:244:18",
                        "location": {
                          "file": "/var/www/360lm/tests/activity.spec.js",
                          "column": 18,
                          "line": 244
                        },
                        "snippet": "  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/activity.spec.js",
                            "column": 18,
                            "line": 244
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n\n  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |\n    at /var/www/360lm/tests/activity.spec.js:244:18"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:28.615Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/activity-Activity-—-Hub-Na-c60c4--home-topbar-has-Hub-button-android-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/activity.spec.js",
                        "column": 18,
                        "line": 244
                      }
                    },
                    {
                      "workerIndex": 1,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 87,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n    at /var/www/360lm/tests/activity.spec.js:244:18",
                        "location": {
                          "file": "/var/www/360lm/tests/activity.spec.js",
                          "column": 18,
                          "line": 244
                        },
                        "snippet": "  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/activity.spec.js",
                            "column": 18,
                            "line": 244
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n\n  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |\n    at /var/www/360lm/tests/activity.spec.js:244:18"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:40:29.516Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/activity-Activity-—-Hub-Na-c60c4--home-topbar-has-Hub-button-android-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/activity.spec.js",
                        "column": 18,
                        "line": 244
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "df521ef6570605d13829-00d54e0526426c598f17",
              "file": "activity.spec.js",
              "line": 239,
              "column": 3
            },
            {
              "title": "sw.js is v25 or later",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 68,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:30.490Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-09bfb43732ec0dd0e518",
              "file": "activity.spec.js",
              "line": 248,
              "column": 3
            },
            {
              "title": "home topbar has Hub button",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 9,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 29,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n    at /var/www/360lm/tests/activity.spec.js:244:18",
                        "location": {
                          "file": "/var/www/360lm/tests/activity.spec.js",
                          "column": 18,
                          "line": 244
                        },
                        "snippet": "  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/activity.spec.js",
                            "column": 18,
                            "line": 244
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n\n  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |\n    at /var/www/360lm/tests/activity.spec.js:244:18"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:10.449Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/activity-Activity-—-Hub-Na-c60c4--home-topbar-has-Hub-button-desktop-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/activity.spec.js",
                        "column": 18,
                        "line": 244
                      }
                    },
                    {
                      "workerIndex": 10,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 77,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n    at /var/www/360lm/tests/activity.spec.js:244:18",
                        "location": {
                          "file": "/var/www/360lm/tests/activity.spec.js",
                          "column": 18,
                          "line": 244
                        },
                        "snippet": "  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/activity.spec.js",
                            "column": 18,
                            "line": 244
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoContain\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // indexOf\u001b[22m\n\nExpected substring: \u001b[32m\"window.location.href='/hub/'\"\u001b[39m\nReceived string:    \u001b[31m\"<!DOCTYPE html>\u001b[39m\n\u001b[31m<html lang=\\\"en\\\">\u001b[39m\n\u001b[31m<head>\u001b[39m\n\u001b[31m<meta charset=\\\"UTF-8\\\">\u001b[39m\n\u001b[31m<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,viewport-fit=cover\\\">\u001b[39m\n\u001b[31m<meta name=\\\"theme-color\\\" content=\\\"#0d9488\\\">\u001b[39m\n\u001b[31m<meta name=\\\"mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-capable\\\" content=\\\"yes\\\">\u001b[39m\n\u001b[31m<meta name=\\\"apple-mobile-web-app-status-bar-style\\\" content=\\\"black-translucent\\\">\u001b[39m\n\u001b[31m<title>360LM Activity</title>\u001b[39m\n\u001b[31m<link rel=\\\"manifest\\\" href=\\\"/activity/manifest.json\\\">\u001b[39m\n\u001b[31m<link rel=\\\"stylesheet\\\" href=\\\"/shared/safe-bottom.css\\\">\u001b[39m\n\u001b[31m<style>\u001b[39m\n\u001b[31m:root {\u001b[39m\n\u001b[31m  --bg: #0f172a; --bg2: #1e293b; --bg3: #334155;\u001b[39m\n\u001b[31m  --teal: #0d9488; --teal2: #0f766e; --teal3: #14b8a6;\u001b[39m\n\u001b[31m  --text: #f1f5f9; --muted: #94a3b8; --border: #334155;\u001b[39m\n\u001b[31m  --red: #ef4444; --green: #22c55e; --amber: #f59e0b;\u001b[39m\n\u001b[31m  --hot: #ef4444; --warm: #f59e0b; --cold: #3b82f6;\u001b[39m\n\u001b[31m  --radius: 12px; --safe-top: env(safe-area-inset-top,0px);\u001b[39m\n\u001b[31m  --safe-bot: env(safe-area-inset-bottom,0px);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\u001b[39m\n\u001b[31mhtml, body { height: 100%; overflow: hidden; background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }\u001b[39m\n\u001b[31minput, textarea, select, button { font: inherit; }\u001b[39m\n\u001b[31mbutton { cursor: pointer; border: none; }·\u001b[39m\n\u001b[31m/* ── Screens ──────────────────────────────── */\u001b[39m\n\u001b[31m.scr { position: fixed; inset: 0; display: none; flex-direction: column; background: var(--bg); overflow: hidden; }\u001b[39m\n\u001b[31m.scr.active { display: flex; }·\u001b[39m\n\u001b[31m/* ── Top bar ──────────────────────────────── */\u001b[39m\n\u001b[31m.topbar {\u001b[39m\n\u001b[31m  display: flex; align-items: center; gap: 12px;\u001b[39m\n\u001b[31m  padding: calc(var(--safe-top) + 12px) 16px 12px;\u001b[39m\n\u001b[31m  background: var(--bg2); border-bottom: 1px solid var(--border);\u001b[39m\n\u001b[31m  flex-shrink: 0;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m.topbar h1 { font-size: 1.1rem; font-weight: 600; flex: 1; }\u001b[39m\n\u001b[31m.topbar .sub { font-size: .8rem; color: var(--muted); }\u001b[39m\n\u001b[31m.back-btn { background: none; color: var(--teal3); font-size: 1.4rem; padding: 4px; }\u001b[39m\n\u001b[31m.icon-btn { background: none; color: var(--muted); font-size: 1.3rem; padding: 4px; }·\u001b[39m\n\u001b[31m/* ── Scrollable body ──────────────────────── */\u001b[39m\n\u001b[31m.scr-body { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; padding: 16px; }\u001b[39m\n\u001b[31m.scr-body.pad0 { padding: 0; }·\u001b[39m\n\u001b[31m/* ── Bottom safe area ─────────────────────── */\u001b[39m\n\u001b[31m.safe-bot { height: var(--safe-bot); flex-shrink: 0; }·\u001b[39m\n\u001b[31m/* ── LOGIN ────────────────────────────────── */\u001b[39m\n\u001b[31m#scr-login { justify-content: center; align-items: center; gap: 0; }\u001b[39m\n\u001b[31m.login-box { width: 100%; max-width: 360px; padding: 0 24px; }\u001b[39m\n\u001b[31m.login-logo { text-align: center; margin-bottom: 32px; }\u001b[39m\n\u001b[31m.login-logo .icon { font-size: 3.5rem; }\u001b[39m\n\u001b[31m.login-logo h1 { font-size: 1.6rem; font-weight: 700; color: var(--teal3); margin-top: 8px; }\u001b[39m\n\u001b[31m.login-logo p { color: var(--muted); font-size: .85rem; margin-top: 4px; }·\u001b[39m\n\u001b[31m.login-tabs { display: flex; border-radius: var(--radius); background: var(--bg2); border: 1px solid var(--border); overflow: hidden; margin-bottom: 24px; }\u001b[39m\n\u001b[31m.login-tab { flex: 1; padding: 10px; text-align: center; font-size: .85rem; background: none; color: var(--muted); transition: .2s; }\u001b[39m\n\u001b[31m.login-tab.active { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m.pin-display { display: flex; justify-content: center; gap: 12px; margin: 20px 0; }\u001b[39m\n\u001b[31m.pin-dot { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--border); background: none; transition: .15s; }\u001b[39m\n\u001b[31m.pin-dot.filled { background: var(--teal3); border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.keypad { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }\u001b[39m\n\u001b[31m.key { background: var(--bg2); color: var(--text); padding: 16px; border-radius: var(--radius); font-size: 1.4rem; font-weight: 500; border: 1px solid var(--border); transition: .15s; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.key:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.key.del { font-size: 1rem; color: var(--muted); }\u001b[39m\n\u001b[31m.key.zero { grid-column: 2; }·\u001b[39m\n\u001b[31m.code-input-wrap { margin-bottom: 20px; }\u001b[39m\n\u001b[31m.code-input-wrap label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.code-input { width: 100%; padding: 14px; background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text); font-size: 1.1rem; text-align: center; letter-spacing: 4px; }\u001b[39m\n\u001b[31m.code-input:focus { outline: none; border-color: var(--teal3); }·\u001b[39m\n\u001b[31m.login-err { color: var(--red); font-size: .85rem; text-align: center; min-height: 20px; margin-top: 8px; }\u001b[39m\n\u001b[31m.btn-primary { width: 100%; padding: 14px; background: var(--teal); color: #fff; border-radius: var(--radius); font-size: 1rem; font-weight: 600; margin-top: 12px; transition: .2s; }\u001b[39m\n\u001b[31m.btn-primary:active { background: var(--teal2); }\u001b[39m\n\u001b[31m.btn-primary:disabled { opacity: .4; }·\u001b[39m\n\u001b[31m/* ── CARDS ────────────────────────────────── */\u001b[39m\n\u001b[31m.card { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); padding: 16px; margin-bottom: 12px; cursor: pointer; transition: .15s; }\u001b[39m\n\u001b[31m.card:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.card-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.card-meta { font-size: .8rem; color: var(--muted); display: flex; gap: 12px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.card-meta span { display: flex; align-items: center; gap: 4px; }·\u001b[39m\n\u001b[31m/* ── BADGES ───────────────────────────────── */\u001b[39m\n\u001b[31m.badge { display: inline-block; padding: 2px 8px; border-radius: 6px; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; }\u001b[39m\n\u001b[31m.badge-teal { background: rgba(13,148,136,.2); color: var(--teal3); }\u001b[39m\n\u001b[31m.badge-red { background: rgba(239,68,68,.2); color: var(--red); }\u001b[39m\n\u001b[31m.badge-amber { background: rgba(245,158,11,.2); color: var(--amber); }\u001b[39m\n\u001b[31m.badge-blue { background: rgba(59,130,246,.2); color: #60a5fa; }\u001b[39m\n\u001b[31m.badge-green { background: rgba(34,197,94,.2); color: var(--green); }\u001b[39m\n\u001b[31m.badge-hot { background: rgba(239,68,68,.2); color: var(--hot); }\u001b[39m\n\u001b[31m.badge-warm { background: rgba(245,158,11,.2); color: var(--warm); }\u001b[39m\n\u001b[31m.badge-cold { background: rgba(59,130,246,.2); color: var(--cold); }·\u001b[39m\n\u001b[31m/* ── ACTIVITY DETAIL ──────────────────────── */\u001b[39m\n\u001b[31m.act-header { background: var(--bg2); padding: 20px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0; }\u001b[39m\n\u001b[31m.act-title { font-size: 1.2rem; font-weight: 700; margin-bottom: 8px; }\u001b[39m\n\u001b[31m.act-meta-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }\u001b[39m\n\u001b[31m.act-stats { display: flex; gap: 12px; margin-top: 12px; }\u001b[39m\n\u001b[31m.act-stat { text-align: center; flex: 1; background: var(--bg3); border-radius: 8px; padding: 10px 8px; }\u001b[39m\n\u001b[31m.act-stat .val { font-size: 1.4rem; font-weight: 700; color: var(--teal3); }\u001b[39m\n\u001b[31m.act-stat .lbl { font-size: .7rem; color: var(--muted); margin-top: 2px; }\u001b[39m\n\u001b[31m.act-actions { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.btn-sec { padding: 13px; border-radius: var(--radius); font-size: .95rem; font-weight: 600; border: 1px solid var(--border); background: var(--bg2); color: var(--text); text-align: center; }\u001b[39m\n\u001b[31m.btn-sec:active { background: var(--bg3); }\u001b[39m\n\u001b[31m.btn-outline-teal { border-color: var(--teal); color: var(--teal3); background: rgba(13,148,136,.08); }\u001b[39m\n\u001b[31m.divider { height: 1px; background: var(--border); margin: 4px 0; }·\u001b[39m\n\u001b[31m/* ── FORM ─────────────────────────────────── */\u001b[39m\n\u001b[31m.form-group { margin-bottom: 16px; }\u001b[39m\n\u001b[31m.form-label { font-size: .82rem; color: var(--muted); margin-bottom: 6px; display: flex; align-items: center; gap: 4px; }\u001b[39m\n\u001b[31m.req { color: var(--red); }\u001b[39m\n\u001b[31m.form-input { width: 100%; padding: 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .95rem; }\u001b[39m\n\u001b[31m.form-input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.form-input::placeholder { color: var(--bg3); }\u001b[39m\n\u001b[31mtextarea.form-input { min-height: 80px; resize: vertical; }\u001b[39m\n\u001b[31mselect.form-input { appearance: none; -webkit-appearance: none; background-image: url(\\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\\\"); background-repeat: no-repeat; background-position: right 12px center; }·\u001b[39m\n\u001b[31m/* ── PHOTO SECTION ────────────────────────── */\u001b[39m\n\u001b[31m.photo-section { margin-top: 8px; }\u001b[39m\n\u001b[31m.photo-section-hdr { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.photo-section-hdr h3 { font-size: .9rem; font-weight: 600; color: var(--muted); }\u001b[39m\n\u001b[31m.photo-quality-badge { font-size: .72rem; color: var(--teal3); background: rgba(13,148,136,.1); padding: 2px 8px; border-radius: 6px; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }\u001b[39m\n\u001b[31m.photo-slot { aspect-ratio: 1; border-radius: 10px; overflow: hidden; border: 1.5px dashed var(--border); background: var(--bg2); position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; }\u001b[39m\n\u001b[31m.photo-slot.has-photo { border-style: solid; border-color: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .add-icon { font-size: 1.4rem; color: var(--bg3); }\u001b[39m\n\u001b[31m.photo-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }\u001b[39m\n\u001b[31m.photo-slot .photo-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0); transition: .15s; }\u001b[39m\n\u001b[31m.photo-slot:active .photo-overlay { background: rgba(0,0,0,.3); }\u001b[39m\n\u001b[31m.photo-slot .hd-btn { position: absolute; bottom: 2px; left: 2px; background: rgba(0,0,0,.7); color: #fff; font-size: .55rem; padding: 1px 4px; border-radius: 4px; font-weight: 700; }\u001b[39m\n\u001b[31m.photo-slot .hd-btn.on { background: var(--teal); }\u001b[39m\n\u001b[31m.photo-slot .rm-btn { position: absolute; top: 2px; right: 2px; background: rgba(239,68,68,.8); color: #fff; font-size: .65rem; width: 16px; height: 16px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }·\u001b[39m\n\u001b[31m/* ── SUBMIT ROW ───────────────────────────── */\u001b[39m\n\u001b[31m.submit-row { padding: 16px; flex-shrink: 0; background: var(--bg); border-top: 1px solid var(--border); }\u001b[39m\n\u001b[31m.submit-row .btn-primary { margin-top: 0; }\u001b[39m\n\u001b[31m.gform-status { display: flex; align-items: center; gap: 6px; font-size: .75rem; color: var(--muted); margin-top: 8px; }·\u001b[39m\n\u001b[31m/* ── RESPONSE LIST ────────────────────────── */\u001b[39m\n\u001b[31m.search-bar { position: relative; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.search-bar input { width: 100%; padding: 10px 12px 10px 36px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.search-bar input:focus { outline: none; border-color: var(--teal3); }\u001b[39m\n\u001b[31m.search-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); font-size: 1rem; pointer-events: none; }\u001b[39m\n\u001b[31m.filter-rows-wrap { margin-bottom: 8px; }\u001b[39m\n\u001b[31m.filter-section { margin-bottom: 4px; }\u001b[39m\n\u001b[31m.filter-label { font-size: .68rem; color: var(--muted); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 3px; padding-left: 2px; }\u001b[39m\n\u001b[31m.filter-row { display: flex; gap: 8px; margin-bottom: 10px; overflow-x: auto; padding-bottom: 4px; padding-top: 8px; }\u001b[39m\n\u001b[31m.filter-chip { padding: 5px 12px; border-radius: 20px; border: 1px solid var(--border); background: var(--bg2); color: var(--muted); font-size: .8rem; white-space: nowrap; cursor: pointer; position: relative; }\u001b[39m\n\u001b[31m.filter-chip.active { background: var(--teal); border-color: var(--teal); color: #fff; }\u001b[39m\n\u001b[31m.filter-chip.conflict { background: rgba(239,68,68,.12); border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m.filter-chip.unavailable { opacity: .38; }\u001b[39m\n\u001b[31m.filter-chip .seq-badge { position: absolute; top: -6px; right: -6px; background: #6366f1; color: #fff; border-radius: 50%; width: 15px; height: 15px; font-size: .58rem; display: flex; align-items: center; justify-content: center; font-weight: 700; line-height: 1; pointer-events: none; }\u001b[39m\n\u001b[31m.resp-card { display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.resp-card-main { flex: 1; min-width: 0; }\u001b[39m\n\u001b[31m.resp-card-main .name { font-weight: 600; font-size: .95rem; }\u001b[39m\n\u001b[31m.resp-card-main .company { font-size: .82rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\u001b[39m\n\u001b[31m.resp-card-side { text-align: right; flex-shrink: 0; }\u001b[39m\n\u001b[31m.resp-card-side .date { font-size: .72rem; color: var(--muted); margin-top: 4px; }\u001b[39m\n\u001b[31m.photo-count-badge { font-size: .72rem; color: var(--muted); margin-top: 4px; }·\u001b[39m\n\u001b[31m/* ── PRESENTATION ─────────────────────────── */\u001b[39m\n\u001b[31m.pres-body { padding: 0; }\u001b[39m\n\u001b[31m.pres-hero { background: linear-gradient(135deg, var(--teal2), #0f4c75); padding: 24px 20px 20px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.pres-hero-name { font-size: 1.3rem; font-weight: 700; margin-bottom: 4px; }\u001b[39m\n\u001b[31m.pres-hero-company { font-size: 1rem; color: rgba(255,255,255,.8); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-hero-meta { display: flex; gap: 8px; flex-wrap: wrap; }\u001b[39m\n\u001b[31m.pres-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.pres-section h3 { font-size: .75rem; text-transform: uppercase; letter-spacing: 1px; color: var(--muted); margin-bottom: 12px; }\u001b[39m\n\u001b[31m.pres-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\u001b[39m\n\u001b[31m.pres-field { }\u001b[39m\n\u001b[31m.pres-field .lbl { font-size: .72rem; color: var(--muted); margin-bottom: 2px; }\u001b[39m\n\u001b[31m.pres-field .val { font-size: .9rem; font-weight: 500; }\u001b[39m\n\u001b[31m.pres-field.wide { grid-column: 1 / -1; }\u001b[39m\n\u001b[31m.pres-photos { display: flex; flex-wrap: wrap; gap: 8px; padding: 16px 20px; box-sizing: border-box; width: 100%; max-width: 100%; overflow: hidden; }\u001b[39m\n\u001b[31m.pres-photo { width: calc(50% - 4px); flex-shrink: 0; border-radius: 10px; overflow: hidden; aspect-ratio: 4/3; cursor: pointer; }\u001b[39m\n\u001b[31m.pres-photo img { width: 100%; height: 100%; object-fit: cover; display: block; max-width: 100%; }\u001b[39m\n\u001b[31m.pres-photo.full { width: 100%; }·\u001b[39m\n\u001b[31m/* ── PHOTO LIGHTBOX ───────────────────────── */\u001b[39m\n\u001b[31m#lightbox { position: fixed; inset: 0; background: rgba(0,0,0,.95); z-index: 1000; display: none; align-items: center; justify-content: center; flex-direction: column; }\u001b[39m\n\u001b[31m#lightbox.open { display: flex; }\u001b[39m\n\u001b[31m#lightbox img { max-width: 100%; max-height: 85vh; object-fit: contain; border-radius: 8px; }\u001b[39m\n\u001b[31m#lightbox .close-lb { position: absolute; top: calc(var(--safe-top) + 16px); right: 20px; color: #fff; font-size: 1.8rem; background: none; }\u001b[39m\n\u001b[31m#lightbox .lb-nav { display: flex; gap: 24px; margin-top: 16px; }\u001b[39m\n\u001b[31m#lightbox .lb-nav button { background: rgba(255,255,255,.15); color: #fff; padding: 8px 20px; border-radius: 8px; font-size: 1rem; }·\u001b[39m\n\u001b[31m/* ── SETTINGS MODAL ───────────────────────── */\u001b[39m\n\u001b[31m.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 500; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.modal-overlay.open { display: flex; }\u001b[39m\n\u001b[31m.modal-sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.modal-sheet h2 { font-size: 1rem; font-weight: 700; margin-bottom: 20px; }\u001b[39m\n\u001b[31m.sheet-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.sheet-row:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.sheet-row .lbl { font-size: .9rem; }\u001b[39m\n\u001b[31m.sheet-row .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31minput[type=range] { width: 120px; accent-color: var(--teal3); }\u001b[39m\n\u001b[31m.btn-danger { color: var(--red); background: rgba(239,68,68,.1); border: 1px solid rgba(239,68,68,.3); padding: 8px 16px; border-radius: 8px; font-size: .85rem; }·\u001b[39m\n\u001b[31m/* ── ADMIN PANEL (team + client access) ────── */\u001b[39m\n\u001b[31m.admin-section { background: var(--bg2); border-radius: var(--radius); border: 1px solid var(--border); margin-bottom: 12px; overflow: hidden; }\u001b[39m\n\u001b[31m.admin-section-hdr { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; }\u001b[39m\n\u001b[31m.admin-section-hdr h3 { font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.admin-item { padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 12px; }\u001b[39m\n\u001b[31m.admin-item:last-child { border-bottom: none; }\u001b[39m\n\u001b[31m.admin-item .name { font-size: .9rem; font-weight: 500; flex: 1; }\u001b[39m\n\u001b[31m.admin-item .sub { font-size: .75rem; color: var(--muted); }\u001b[39m\n\u001b[31m.small-btn { padding: 4px 10px; border-radius: 6px; font-size: .75rem; border: 1px solid var(--border); background: var(--bg3); color: var(--muted); cursor: pointer; }\u001b[39m\n\u001b[31m.small-btn-red { border-color: rgba(239,68,68,.4); color: var(--red); background: rgba(239,68,68,.1); }·\u001b[39m\n\u001b[31m/* ── INLINE MODAL ─────────────────────────── */\u001b[39m\n\u001b[31m.inline-modal { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 600; display: none; align-items: flex-end; }\u001b[39m\n\u001b[31m.inline-modal.open { display: flex; }\u001b[39m\n\u001b[31m.inline-modal .sheet { background: var(--bg2); border-radius: 20px 20px 0 0; width: 100%; padding: 20px 20px calc(20px + var(--safe-bot)); }\u001b[39m\n\u001b[31m.inline-modal h3 { font-size: 1rem; font-weight: 700; margin-bottom: 16px; }\u001b[39m\n\u001b[31m.modal-row { margin-bottom: 14px; }\u001b[39m\n\u001b[31m.modal-row label { font-size: .8rem; color: var(--muted); display: block; margin-bottom: 6px; }\u001b[39m\n\u001b[31m.modal-row input, .modal-row select, .modal-row textarea { width: 100%; padding: 10px 12px; background: var(--bg3); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: .9rem; }\u001b[39m\n\u001b[31m.modal-row textarea { resize: vertical; min-height: 72px; }\u001b[39m\n\u001b[31m.modal-actions { display: flex; gap: 10px; margin-top: 16px; }\u001b[39m\n\u001b[31m.modal-actions button { flex: 1; padding: 12px; border-radius: 10px; font-size: .9rem; font-weight: 600; }\u001b[39m\n\u001b[31m.modal-cancel { background: var(--bg3); color: var(--muted); }\u001b[39m\n\u001b[31m.modal-ok { background: var(--teal); color: #fff; }·\u001b[39m\n\u001b[31m/* ── TOAST ────────────────────────────────── */\u001b[39m\n\u001b[31m#toast { position: fixed; bottom: calc(24px + var(--safe-bot)); left: 50%; transform: translateX(-50%) translateY(80px); background: var(--bg2); color: var(--text); padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); font-size: .85rem; transition: .3s; z-index: 900; white-space: nowrap; box-shadow: 0 4px 20px rgba(0,0,0,.4); pointer-events: none; }\u001b[39m\n\u001b[31m#toast.show { transform: translateX(-50%) translateY(0); }\u001b[39m\n\u001b[31m#toast.err { border-color: var(--red); color: var(--red); }\u001b[39m\n\u001b[31m#toast.ok { border-color: var(--green); color: var(--green); }\u001b[39m\n\u001b[31m#update-banner { display:none; position:fixed; top:0; left:0; right:0; z-index:600; background:#0f2a26; border-bottom:2px solid var(--teal); padding:10px 16px; align-items:center; gap:12px; }\u001b[39m\n\u001b[31m#update-banner.show { display:flex; }\u001b[39m\n\u001b[31m#update-banner .ub-text { flex:1; font-size:.85rem; font-weight:600; color:var(--text); }\u001b[39m\n\u001b[31m#update-banner .ub-sub { font-size:.72rem; color:var(--muted); margin-top:2px; }\u001b[39m\n\u001b[31m#update-banner .ub-btn { background:var(--teal); color:#fff; border:none; border-radius:8px; padding:7px 14px; font-size:.8rem; font-weight:700; cursor:pointer; flex-shrink:0; }\u001b[39m\n\u001b[31m#update-banner .ub-close { background:none; border:none; color:var(--muted); font-size:18px; cursor:pointer; padding:4px; flex-shrink:0; }·\u001b[39m\n\u001b[31m/* ── AUDIT PANEL ──────────────────────────── */\u001b[39m\n\u001b[31m.audit-panel { background: rgba(99,102,241,.07); border: 1px solid rgba(99,102,241,.2); border-radius: 10px; padding: 12px 14px; }\u001b[39m\n\u001b[31m.audit-row { display: flex; align-items: baseline; gap: 8px; font-size: .8rem; }\u001b[39m\n\u001b[31m.audit-row + .audit-row { margin-top: 8px; }\u001b[39m\n\u001b[31m.audit-row .audit-lbl { color: var(--muted); min-width: 74px; flex-shrink: 0; }\u001b[39m\n\u001b[31m.audit-row .audit-val { color: var(--text); font-weight: 500; }·\u001b[39m\n\u001b[31m/* ── SPINNER ─────────────────────────────── */\u001b[39m\n\u001b[31m.spinner { display: inline-block; width: 20px; height: 20px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .6s linear infinite; }\u001b[39m\n\u001b[31m@keyframes spin { to { transform: rotate(360deg); } }·\u001b[39m\n\u001b[31m/* ── EMPTY STATE ─────────────────────────── */\u001b[39m\n\u001b[31m.empty { text-align: center; padding: 48px 24px; color: var(--muted); }\u001b[39m\n\u001b[31m.empty .ico { font-size: 2.5rem; margin-bottom: 12px; }\u001b[39m\n\u001b[31m.empty p { font-size: .9rem; }·\u001b[39m\n\u001b[31m/* ── STATS PANEL ─────────────────────────── */\u001b[39m\n\u001b[31m.stats-panel { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 14px 16px; flex-shrink: 0; max-height: 46vh; overflow-y: auto; }\u001b[39m\n\u001b[31m.stats-panel-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin-bottom: 6px; }\u001b[39m\n\u001b[31m.stat-row { margin-bottom: 7px; }\u001b[39m\n\u001b[31m.stat-row-label { font-size: .8rem; color: var(--text); display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }\u001b[39m\n\u001b[31m.stat-bar-wrap { background: var(--bg3); border-radius: 4px; height: 5px; overflow: hidden; }\u001b[39m\n\u001b[31m.stat-bar { height: 100%; border-radius: 4px; transition: width .3s; }\u001b[39m\n\u001b[31m.stat-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; }·\u001b[39m\n\u001b[31m/* ── CONTACT ACTIONS ─────────────────────── */\u001b[39m\n\u001b[31m.contact-actions { display: flex; gap: 8px; padding: 12px 20px; border-bottom: 1px solid var(--border); }\u001b[39m\n\u001b[31m.contact-btn { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 10px 4px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; color: var(--text); font-size: .7rem; font-weight: 500; cursor: pointer; text-decoration: none; -webkit-tap-highlight-color: transparent; }\u001b[39m\n\u001b[31m.contact-btn .cb-icon { font-size: 1.2rem; }\u001b[39m\n\u001b[31m.contact-btn:active { background: var(--bg3); }·\u001b[39m\n\u001b[31m/* ── LOCATION CAPTURE (in form) ──────────── */\u001b[39m\n\u001b[31m.loc-row { margin-bottom: 14px; padding: 10px 12px; background: var(--bg2); border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; gap: 10px; }\u001b[39m\n\u001b[31m.loc-info { flex: 1; font-size: .82rem; }\u001b[39m\n\u001b[31m.loc-info .loc-coords { color: var(--teal3); font-weight: 600; font-size: .75rem; margin-top: 2px; word-break: break-all; }\u001b[39m\n\u001b[31m.loc-capture-btn { padding: 7px 12px; border-radius: 8px; background: rgba(13,148,136,.15); border: 1px solid rgba(13,148,136,.3); color: var(--teal3); font-size: .78rem; font-weight: 600; cursor: pointer; white-space: nowrap; flex-shrink: 0; }\u001b[39m\n\u001b[31m</style>\u001b[39m\n\u001b[31m</head>\u001b[39m\n\u001b[31m<body>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LOGIN ═══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-login\\\" class=\\\"scr active\\\">\u001b[39m\n\u001b[31m  <div class=\\\"login-box\\\">\u001b[39m\n\u001b[31m    <div class=\\\"login-logo\\\">\u001b[39m\n\u001b[31m      <div class=\\\"icon\\\">🎯</div>\u001b[39m\n\u001b[31m      <h1>360LM Activity</h1>\u001b[39m\n\u001b[31m      <p>Response Capture & Reporting</p>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <p style=\\\"text-align:center;color:var(--muted);font-size:.85rem;margin-bottom:24px\\\">Sign in to continue</p>\u001b[39m\n\u001b[31m    <a href=\\\"/hub/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--teal);color:#fff;border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none;margin-bottom:12px\\\">👤 Employee Login</a>\u001b[39m\n\u001b[31m    <a href=\\\"/client/?next=/activity/\\\" style=\\\"display:block;text-align:center;padding:14px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:var(--radius);font-size:1rem;font-weight:600;text-decoration:none\\\">🏢 Client Access</a>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ HOME ════════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-home\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:1.4rem\\\">🎯</div>\u001b[39m\n\u001b[31m    <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m      <h1>Activities</h1>\u001b[39m\n\u001b[31m      <div class=\\\"sub\\\" id=\\\"home-user-sub\\\"></div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"goToHub()\\\" title=\\\"Hub\\\">🏡</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"logout()\\\" title=\\\"Logout\\\">🔒</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"openSettings()\\\" title=\\\"Settings\\\">⚙️</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"home-list\\\">\u001b[39m\n\u001b[31m    <div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>Loading activities…</p></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ACTIVITY DETAIL ═════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-activity\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showHome()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"act-topbar-title\\\">Activity</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"act-manage-btn\\\" onclick=\\\"showManage()\\\" style=\\\"display:none\\\">👥</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"act-header\\\" id=\\\"act-header\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"act-actions\\\" id=\\\"act-action-btns\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ CAPTURE FORM ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-capture\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" id=\\\"capture-back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1 id=\\\"capture-title\\\">Capture Response</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"cap-sub\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div id=\\\"draft-existing-photos\\\" style=\\\"display:none;padding:16px 20px 0\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"capture-form-body\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"submit-row\\\">\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"submit-btn\\\" onclick=\\\"submitResponse()\\\">Submit Response</button>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec\\\" id=\\\"draft-btn\\\" onclick=\\\"saveDraft()\\\" style=\\\"margin-top:8px\\\">💾 Save as Draft</button>\u001b[39m\n\u001b[31m    <button id=\\\"del-draft-btn\\\" onclick=\\\"confirmDeleteDraft()\\\" style=\\\"display:none;margin-top:8px;width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);cursor:pointer\\\">🗑️ Delete Draft</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ RESPONSES LIST ══════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-responses\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Responses</h1>\u001b[39m\n\u001b[31m    <div class=\\\"sub\\\" id=\\\"resp-count-sub\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"toggleStatsPanel()\\\" title=\\\"Statistics\\\">📊</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div id=\\\"resp-stats-panel\\\" class=\\\"stats-panel\\\" style=\\\"display:none\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\">\u001b[39m\n\u001b[31m    <div class=\\\"search-bar\\\">\u001b[39m\n\u001b[31m      <span class=\\\"search-icon\\\">🔍</span>\u001b[39m\n\u001b[31m      <input type=\\\"search\\\" id=\\\"resp-search\\\" placeholder=\\\"Search name, company…\\\" oninput=\\\"filterResponses()\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"filter-rows-container\\\" class=\\\"filter-rows-wrap\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"resp-list\\\"></div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PRESENTATION ════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-pres\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showResponses()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Response</h1>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" id=\\\"pres-edit-btn\\\" onclick=\\\"openPhotoEdit()\\\" style=\\\"display:none\\\" title=\\\"Edit Photos\\\">📸</button>\u001b[39m\n\u001b[31m    <button class=\\\"icon-btn\\\" onclick=\\\"shareResponse()\\\">📤</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body pad0\\\" id=\\\"pres-body\\\" style=\\\"overflow-y:auto\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ MANAGE ══════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"scr-manage\\\" class=\\\"scr\\\">\u001b[39m\n\u001b[31m  <div class=\\\"topbar\\\">\u001b[39m\n\u001b[31m    <button class=\\\"back-btn\\\" onclick=\\\"showActivity()\\\">‹</button>\u001b[39m\n\u001b[31m    <h1>Manage Access</h1>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <div class=\\\"scr-body\\\" id=\\\"manage-body\\\"></div>\u001b[39m\n\u001b[31m  <div class=\\\"safe-bot\\\"></div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SETTINGS MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"settings-overlay\\\" onclick=\\\"closeSettings()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <h2>⚙️ Settings</h2>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Photo Quality</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"qual-label\\\">Default: 70%</div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <input type=\\\"range\\\" id=\\\"quality-slider\\\" min=\\\"30\\\" max=\\\"100\\\" value=\\\"70\\\" step=\\\"5\\\" oninput=\\\"updateQuality(this.value)\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"sheet-row\\\">\u001b[39m\n\u001b[31m      <div>\u001b[39m\n\u001b[31m        <div class=\\\"lbl\\\">Logged in as</div>\u001b[39m\n\u001b[31m        <div class=\\\"sub\\\" id=\\\"settings-user\\\"></div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <button class=\\\"btn-danger\\\" onclick=\\\"logout()\\\">Sign out</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ AREA CONFIRM MODAL ══════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"modal-overlay\\\" id=\\\"area-confirm-overlay\\\" style=\\\"display:none\\\">\u001b[39m\n\u001b[31m  <div class=\\\"modal-sheet\\\" onclick=\\\"event.stopPropagation()\\\">\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;justify-content:space-between;align-items:center;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <h2 style=\\\"margin:0;font-size:1.1rem\\\">📍 Confirm Area</h2>\u001b[39m\n\u001b[31m      <span id=\\\"area-act-badge\\\" style=\\\"font-size:.75rem;color:var(--muted);max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap\\\"></span>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"filter-row\\\" id=\\\"area-modal-chips\\\" style=\\\"flex-wrap:wrap;margin-bottom:8px\\\"></div>\u001b[39m\n\u001b[31m    <div id=\\\"area-custom-row\\\" style=\\\"display:none;margin-bottom:12px\\\">\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"area-custom-input\\\" class=\\\"form-input\\\" placeholder=\\\"Enter area name…\\\" style=\\\"margin-top:4px\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"area-admin-row\\\" style=\\\"display:none;margin-bottom:8px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" style=\\\"font-size:.8rem;padding:7px 12px;width:100%\\\" onclick=\\\"toggleAreaEdit()\\\">✏️ Edit city list for this activity</button>\u001b[39m\n\u001b[31m      <div id=\\\"area-edit-box\\\" style=\\\"display:none;margin-top:8px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:4px\\\">Cities (comma-separated)</div>\u001b[39m\n\u001b[31m        <input type=\\\"text\\\" id=\\\"area-cities-input\\\" class=\\\"form-input\\\" placeholder=\\\"Mohali, Ludhiana, Jalandhar\\\">\u001b[39m\n\u001b[31m        <button class=\\\"btn-primary\\\" style=\\\"margin-top:8px;width:100%;padding:12px\\\" onclick=\\\"saveCitiesEdit()\\\">💾 Save Cities</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-primary\\\" id=\\\"area-confirm-btn\\\" style=\\\"width:100%;padding:14px;margin-top:4px\\\" onclick=\\\"confirmArea()\\\">Confirm →</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD TEAM MODAL ══════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-team-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Add Team Member</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Employee ID</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"tm-emp-id\\\" placeholder=\\\"e.g. rakesh\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"tm-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Capture (can submit responses)</option>\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View only</option>\u001b[39m\n\u001b[31m        <option value=\\\"admin\\\">Admin (full access)</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddTeam()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveTeamMember()\\\">Add</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD CLIENT ACCESS MODAL ═════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-client-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>Grant Client Access</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-company-id\\\">\u001b[39m\n\u001b[31m        <option value=\\\"\\\">— select company —</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-name\\\" placeholder=\\\"Contact name\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Phone (optional)</label>\u001b[39m\n\u001b[31m      <input type=\\\"tel\\\" id=\\\"ca-phone\\\" placeholder=\\\"+91 98765 43210\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Access Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(auto-generated if blank)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"ca-code\\\" placeholder=\\\"e.g. EPSON2025\\\" maxlength=\\\"12\\\" autocapitalize=\\\"characters\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Role</label>\u001b[39m\n\u001b[31m      <select id=\\\"ca-role\\\">\u001b[39m\n\u001b[31m        <option value=\\\"view\\\">View responses only</option>\u001b[39m\n\u001b[31m        <option value=\\\"capture\\\">Can capture responses</option>\u001b[39m\n\u001b[31m      </select>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeAddClient()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveClientAccess()\\\">Grant Access</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ ADD/EDIT COMPANY MODAL ══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"add-company-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 id=\\\"company-modal-title\\\">Add Company</h3>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Name</label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-name\\\" placeholder=\\\"e.g. Epson India\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Company Code <span style=\\\"color:var(--muted);font-size:.75rem\\\">(clients enter this)</span></label>\u001b[39m\n\u001b[31m      <input type=\\\"text\\\" id=\\\"cm-code\\\" placeholder=\\\"e.g. epson2025\\\" maxlength=\\\"20\\\" autocapitalize=\\\"none\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeCompanyModal()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" onclick=\\\"saveCompany()\\\">Save</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ EDIT RESPONSE MODAL ═════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"edit-response-modal\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>✏️ Edit Response</h3>\u001b[39m\n\u001b[31m    <div id=\\\"edit-form-fields\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closeEditResponse()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"edit-save-btn\\\" onclick=\\\"saveEditResponse()\\\">Save Changes</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ SUBMIT DATETIME MODAL ═══════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"submit-dt-modal\\\" onclick=\\\"if(event.target===this)cancelSubmitDatetime()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3>📅 Set Submission Date & Time</h3>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-draft-info\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px;line-height:1.5\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-row\\\">\u001b[39m\n\u001b[31m      <label>Submission Date & Time (IST)</label>\u001b[39m\n\u001b[31m      <input type=\\\"datetime-local\\\" id=\\\"submit-dt-input\\\" style=\\\"color-scheme:dark\\\">\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div id=\\\"submit-dt-hint\\\" style=\\\"font-size:.72rem;color:var(--muted);margin-top:-8px;margin-bottom:4px\\\"></div>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"cancelSubmitDatetime()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"submit-dt-ok\\\" onclick=\\\"confirmSubmitDatetime()\\\">Confirm & Submit</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ LIGHTBOX ════════════════════════════ -->\u001b[39m\n\u001b[31m<div id=\\\"lightbox\\\">\u001b[39m\n\u001b[31m  <button class=\\\"close-lb\\\" onclick=\\\"closeLightbox()\\\">✕</button>\u001b[39m\n\u001b[31m  <img id=\\\"lb-img\\\" src=\\\"\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m  <div class=\\\"lb-nav\\\">\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(-1)\\\">‹ Prev</button>\u001b[39m\n\u001b[31m    <button onclick=\\\"lbNav(1)\\\">Next ›</button>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO EDIT MODAL ════════════════════ -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-edit-modal\\\" onclick=\\\"if(event.target===this)closePhotoEdit()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\" style=\\\"max-height:82vh;overflow-y:auto\\\">\u001b[39m\n\u001b[31m    <h3>📸 Edit Photos</h3>\u001b[39m\n\u001b[31m    <p style=\\\"font-size:.8rem;color:var(--muted);margin-bottom:14px\\\">Tap ✕ to remove a photo. Teal border = new (not yet saved).</p>\u001b[39m\n\u001b[31m    <div id=\\\"photo-edit-grid\\\" style=\\\"display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px\\\"></div>\u001b[39m\n\u001b[31m    <button class=\\\"btn-sec btn-outline-teal\\\" id=\\\"photo-edit-add-btn\\\" onclick=\\\"addEditPhotoSlot()\\\" style=\\\"margin-bottom:4px\\\">+ Add Photo</button>\u001b[39m\n\u001b[31m    <div class=\\\"modal-actions\\\" style=\\\"margin-top:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"modal-cancel\\\" onclick=\\\"closePhotoEdit()\\\">Cancel</button>\u001b[39m\n\u001b[31m      <button class=\\\"modal-ok\\\" id=\\\"photo-edit-save-btn\\\" onclick=\\\"savePhotoEdit()\\\">Save Photos</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- ════════════════════════════ PHOTO SOURCE DIALOG ══════════════════ -->\u001b[39m\n\u001b[31m<!-- Must be AFTER photo-edit-modal in DOM so it renders on top (same z-index, later = higher) -->\u001b[39m\n\u001b[31m<div class=\\\"inline-modal\\\" id=\\\"photo-src-modal\\\" onclick=\\\"if(event.target===this)cancelPhotoSrc()\\\">\u001b[39m\n\u001b[31m  <div class=\\\"sheet\\\">\u001b[39m\n\u001b[31m    <h3 style=\\\"margin-bottom:20px\\\">Add Photo</h3>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;flex-direction:column;gap:12px\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" style=\\\"margin-top:0\\\" onclick=\\\"pickCamera()\\\">📷 Camera</button>\u001b[39m\n\u001b[31m      <button class=\\\"btn-sec\\\" onclick=\\\"pickGallery()\\\">🖼️ Gallery / Browse Files</button>\u001b[39m\n\u001b[31m      <button style=\\\"padding:12px;border-radius:10px;font-size:.9rem;font-weight:600;background:var(--bg3);color:var(--muted);border:none;cursor:pointer\\\" onclick=\\\"cancelPhotoSrc()\\\">Cancel</button>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m</div>·\u001b[39m\n\u001b[31m<!-- Shared file inputs — camera and gallery (body-level, reused by both capture form and photo edit) -->\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-camera-input\\\" accept=\\\"image/*\\\" capture=\\\"environment\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">\u001b[39m\n\u001b[31m<input type=\\\"file\\\" id=\\\"photo-gallery-input\\\" accept=\\\"image/*\\\" style=\\\"display:none\\\" onchange=\\\"onPhotoChosen(event)\\\">·\u001b[39m\n\u001b[31m<div id=\\\"update-banner\\\">\u001b[39m\n\u001b[31m  <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m    <div class=\\\"ub-text\\\">Update available</div>\u001b[39m\n\u001b[31m    <div class=\\\"ub-sub\\\">Tap to get the latest version</div>\u001b[39m\n\u001b[31m  </div>\u001b[39m\n\u001b[31m  <button class=\\\"ub-btn\\\" id=\\\"btn-update\\\">Update now</button>\u001b[39m\n\u001b[31m  <button class=\\\"ub-close\\\" id=\\\"btn-update-dismiss\\\">✕</button>\u001b[39m\n\u001b[31m</div>\u001b[39m\n\u001b[31m<div id=\\\"toast\\\"></div>·\u001b[39m\n\u001b[31m<script>\u001b[39m\n\u001b[31m'use strict';·\u001b[39m\n\u001b[31m/* ─── Constants ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst API     = '/db';\u001b[39m\n\u001b[31mconst PROXY   = '/sales-proxy';\u001b[39m\n\u001b[31mconst ACT_H   = { 'Content-Type':'application/json','Accept-Profile':'activity','Content-Profile':'activity','Prefer':'return=representation' };\u001b[39m\n\u001b[31mconst ACT_RD  = { 'Content-Type':'application/json','Accept-Profile':'activity' };\u001b[39m\n\u001b[31mconst EXP_RD  = { 'Content-Type':'application/json','Accept-Profile':'expense' };\u001b[39m\n\u001b[31mconst SESSION_KEY = 'lm360-activity-session';\u001b[39m\n\u001b[31mconst QUAL_KEY    = 'lm360-activity-photo-quality';\u001b[39m\n\u001b[31mconst SESSION_TTL = 8 * 3600 * 1000;\u001b[39m\n\u001b[31mconst ADMIN_IDS   = ['harish','pramod'];\u001b[39m\n\u001b[31mconst MAX_PHOTOS  = 5;·\u001b[39m\n\u001b[31m/* ─── IST datetime helpers ───────────────────────────── */\u001b[39m\n\u001b[31mfunction toISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // UTC ts → 'YYYY-MM-DDTHH:MM' in IST (truncates to minute — safe for max/value)\u001b[39m\n\u001b[31m  const ist = new Date(new Date(ts).getTime() + 5.5 * 3600000);\u001b[39m\n\u001b[31m  return ist.toISOString().slice(0, 16);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction ceilMinISTDatetimeLocal(ts) {\u001b[39m\n\u001b[31m  // Like toISTDatetimeLocal but rounds UP to next whole minute when seconds > 0.\u001b[39m\n\u001b[31m  // Use for input.min only: prevents trigger rejection when user picks the floor of draft_at.\u001b[39m\n\u001b[31m  const d = new Date(ts);\u001b[39m\n\u001b[31m  if (d.getUTCSeconds() > 0 || d.getUTCMilliseconds() > 0) {\u001b[39m\n\u001b[31m    d.setUTCSeconds(0, 0);\u001b[39m\n\u001b[31m    d.setUTCMinutes(d.getUTCMinutes() + 1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return toISTDatetimeLocal(d);\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction nowISTDatetimeLocal() { return toISTDatetimeLocal(new Date()); }\u001b[39m\n\u001b[31mfunction istInputToISO(val) {\u001b[39m\n\u001b[31m  // 'YYYY-MM-DDTHH:MM' treated as IST → UTC ISO string\u001b[39m\n\u001b[31m  if (!val) return null;\u001b[39m\n\u001b[31m  const [dp, tp] = val.split('T');\u001b[39m\n\u001b[31m  const [y, m, d] = dp.split('-').map(Number);\u001b[39m\n\u001b[31m  const [h, mn] = tp.split(':').map(Number);\u001b[39m\n\u001b[31m  return new Date(Date.UTC(y, m - 1, d, h, mn) - 5.5 * 3600000).toISOString();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction fmtISTDatetime(ts) {\u001b[39m\n\u001b[31m  if (!ts) return '—';\u001b[39m\n\u001b[31m  return new Date(ts).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m    day: 'numeric', month: 'short', year: 'numeric',\u001b[39m\n\u001b[31m    hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m  }) + ' IST';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── State ─────────────────────────────────────────────── */\u001b[39m\n\u001b[31mlet session = null;       // { id, name, role, type:'employee'|'client', activityId? }\u001b[39m\n\u001b[31mlet activities = [];\u001b[39m\n\u001b[31mlet currentActivity = null;\u001b[39m\n\u001b[31mlet currentResponses = [];\u001b[39m\n\u001b[31mlet filteredResponses = [];\u001b[39m\n\u001b[31mlet currentResponse = null;\u001b[39m\n\u001b[31mlet lbPhotos = [], lbIdx = 0;\u001b[39m\n\u001b[31mlet photos = [];          // Array of {blob, url, hdMode, file}\u001b[39m\n\u001b[31mlet editingDraftId = null;\u001b[39m\n\u001b[31mlet draftExistingPhotos = [];\u001b[39m\n\u001b[31mlet draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31mlet pendingArea = '';\u001b[39m\n\u001b[31mlet pendingLocation = '';\u001b[39m\n\u001b[31mlet statsVisible = false;\u001b[39m\n\u001b[31mlet _areaModalChosen = '';\u001b[39m\n\u001b[31mlet clientCompanies = [];\u001b[39m\n\u001b[31mlet editingCompanyId = null;·\u001b[39m\n\u001b[31m/* ─── Init ──────────────────────────────────────────────── */\u001b[39m\n\u001b[31mconst savedQ = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31mdocument.getElementById('quality-slider').value = savedQ;\u001b[39m\n\u001b[31mdocument.getElementById('qual-label').textContent = `Default: ${savedQ}%`;·\u001b[39m\n\u001b[31m(function restoreSession() {\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const raw = localStorage.getItem(SESSION_KEY);\u001b[39m\n\u001b[31m    if (raw) {\u001b[39m\n\u001b[31m      const s = JSON.parse(raw);\u001b[39m\n\u001b[31m      if (Date.now() - s.ts <= SESSION_TTL) {\u001b[39m\n\u001b[31m        session = s;\u001b[39m\n\u001b[31m        document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m        return;\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Auto-login from Hub session — avoids re-login when already logged in via Hub\u001b[39m\n\u001b[31m    const hub = JSON.parse(localStorage.getItem('lm360-session') || 'null');\u001b[39m\n\u001b[31m    if (hub && hub.empId && hub.name && hub.loginAt) {\u001b[39m\n\u001b[31m      if (Date.now() - new Date(hub.loginAt).getTime() <= 12 * 3600 * 1000) {\u001b[39m\n\u001b[31m        saveSession({ id: hub.empId, name: hub.name, role: hub.role || 'staff', type: 'employee' });\u001b[39m\n\u001b[31m        showHome();\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  } catch(_) { localStorage.removeItem(SESSION_KEY); }\u001b[39m\n\u001b[31m})();·\u001b[39m\n\u001b[31mfunction saveSession(s) {\u001b[39m\n\u001b[31m  s.ts = Date.now();\u001b[39m\n\u001b[31m  session = s;\u001b[39m\n\u001b[31m  localStorage.setItem(SESSION_KEY, JSON.stringify(s));\u001b[39m\n\u001b[31m  // ADR-112: mirror to the shared session key too — a direct login here must be\u001b[39m\n\u001b[31m  // recognized by Hub and every other PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m    empId: s.id, name: s.name, role: s.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m  }));\u001b[39m\n\u001b[31m  document.getElementById('settings-user').textContent = s.name;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Navigation ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction show(id) {\u001b[39m\n\u001b[31m  document.querySelectorAll('.scr').forEach(s => s.classList.remove('active'));\u001b[39m\n\u001b[31m  document.getElementById(id).classList.add('active');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction showHome() { show('scr-home'); loadActivities(); }\u001b[39m\n\u001b[31mfunction showActivity() { show('scr-activity'); }\u001b[39m\n\u001b[31mfunction showCapture() { show('scr-capture'); buildCaptureForm(); }\u001b[39m\n\u001b[31mfunction showResponses() { show('scr-responses'); loadResponses(); }\u001b[39m\n\u001b[31mfunction showPresentation(r) { currentResponse = r; show('scr-pres'); buildPresentation(r); }\u001b[39m\n\u001b[31mfunction showManage() { show('scr-manage'); buildManage(); }\u001b[39m\n\u001b[31mfunction directManage(id) { currentActivity = activities.find(a => a.id === id); if (currentActivity) showManage(); }·\u001b[39m\n\u001b[31m/* ─── Area Confirmation Modal ───────────────────────────── */\u001b[39m\n\u001b[31mfunction showAreaConfirm() {\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _areaModalChosen = pendingArea || (cities[0] || '');\u001b[39m\n\u001b[31m  document.getElementById('area-act-badge').textContent =\u001b[39m\n\u001b[31m    currentActivity.title.length > 22 ? currentActivity.title.slice(0,22)+'…' : currentActivity.title;\u001b[39m\n\u001b[31m  const isAdmin = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m  document.getElementById('area-admin-row').style.display = isAdmin ? '' : 'none';\u001b[39m\n\u001b[31m  if (isAdmin) {\u001b[39m\n\u001b[31m    document.getElementById('area-cities-input').value = currentActivity.city || '';\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'flex';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction _renderAreaChips(cities) {\u001b[39m\n\u001b[31m  const row = document.getElementById('area-modal-chips');\u001b[39m\n\u001b[31m  const other = _areaModalChosen === '__other__' || (cities.length > 0 && !cities.includes(_areaModalChosen) && _areaModalChosen !== '');\u001b[39m\n\u001b[31m  const effectiveOther = other || cities.length === 0;\u001b[39m\n\u001b[31m  row.innerHTML = cities.map(c =>\u001b[39m\n\u001b[31m    `<button class=\\\"filter-chip${_areaModalChosen===c?' active':''}\\\" onclick=\\\"selectAreaChip('${esc(c)}')\\\">${esc(c)}</button>`\u001b[39m\n\u001b[31m  ).join('') + `<button class=\\\"filter-chip${effectiveOther?' active':''}\\\" onclick=\\\"selectAreaChip('__other__')\\\">Other…</button>`;\u001b[39m\n\u001b[31m  const customRow = document.getElementById('area-custom-row');\u001b[39m\n\u001b[31m  customRow.style.display = effectiveOther ? '' : 'none';\u001b[39m\n\u001b[31m  if (effectiveOther) document.getElementById('area-custom-input').value = (other && _areaModalChosen !== '__other__') ? _areaModalChosen : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction selectAreaChip(val) {\u001b[39m\n\u001b[31m  _areaModalChosen = val;\u001b[39m\n\u001b[31m  const cities = (currentActivity.city || '').split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m  _renderAreaChips(cities);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaEdit() {\u001b[39m\n\u001b[31m  const box = document.getElementById('area-edit-box');\u001b[39m\n\u001b[31m  box.style.display = box.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveCitiesEdit() {\u001b[39m\n\u001b[31m  const newCities = document.getElementById('area-cities-input').value.trim();\u001b[39m\n\u001b[31m  const btn = document.querySelector('#area-edit-box .btn-primary');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(currentActivity.id)}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: { ...ACT_H, 'Prefer': 'return=minimal' }, body: JSON.stringify({ city: newCities })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error(`HTTP ${r.status}`);\u001b[39m\n\u001b[31m    currentActivity.city = newCities;\u001b[39m\n\u001b[31m    const cities = newCities.split(',').map(c => c.trim()).filter(Boolean);\u001b[39m\n\u001b[31m    _renderAreaChips(cities);\u001b[39m\n\u001b[31m    document.getElementById('area-edit-box').style.display = 'none';\u001b[39m\n\u001b[31m    toast('Cities saved.', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Failed to save cities.', 'err');\u001b[39m\n\u001b[31m  } finally {\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save Cities';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction captureLocation() {\u001b[39m\n\u001b[31m  if (!navigator.geolocation) { toast('Geolocation not supported on this device', 'err'); return; }\u001b[39m\n\u001b[31m  const btn = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.textContent = '⏳ Getting…'; btn.disabled = true; }\u001b[39m\n\u001b[31m  navigator.geolocation.getCurrentPosition(\u001b[39m\n\u001b[31m    pos => {\u001b[39m\n\u001b[31m      pendingLocation = `${pos.coords.latitude.toFixed(5)},${pos.coords.longitude.toFixed(5)}`;\u001b[39m\n\u001b[31m      const acc = Math.round(pos.coords.accuracy);\u001b[39m\n\u001b[31m      const row = document.getElementById('loc-row');\u001b[39m\n\u001b[31m      if (row) row.innerHTML = `\u001b[39m\n\u001b[31m        <div class=\\\"loc-info\\\">\u001b[39m\n\u001b[31m          <div>📍 Your Location</div>\u001b[39m\n\u001b[31m          <div class=\\\"loc-coords\\\">${esc(pendingLocation)} · ±${acc}m</div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">🔄 Re-capture</button>`;\u001b[39m\n\u001b[31m      toast('Location captured', 'ok');\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    () => {\u001b[39m\n\u001b[31m      toast('Could not get location — check permissions', 'err');\u001b[39m\n\u001b[31m      const b = document.querySelector('.loc-capture-btn');\u001b[39m\n\u001b[31m      if (b) { b.textContent = 'Capture Location'; b.disabled = false; }\u001b[39m\n\u001b[31m    },\u001b[39m\n\u001b[31m    { timeout: 10000, enableHighAccuracy: true }\u001b[39m\n\u001b[31m  );\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmArea() {\u001b[39m\n\u001b[31m  let area = _areaModalChosen;\u001b[39m\n\u001b[31m  if (area === '__other__' || area === '') {\u001b[39m\n\u001b[31m    area = (document.getElementById('area-custom-input').value || '').trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  pendingArea = area;\u001b[39m\n\u001b[31m  document.getElementById('area-confirm-overlay').style.display = 'none';\u001b[39m\n\u001b[31m  showCapture();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Home — Activity List ──────────────────────────────── */\u001b[39m\n\u001b[31masync function loadActivities() {\u001b[39m\n\u001b[31m  const el = document.getElementById('home-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('home-user-sub');\u001b[39m\n\u001b[31m  sub.textContent = session.name;\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let acts = [];\u001b[39m\n\u001b[31m    if (session.type === 'client') {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?id=eq.${encodeURIComponent(session.activityId)}&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else if (ADMIN_IDS.includes(session.id)) {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activities?active=eq.true&order=created_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      acts = await r.json();\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const r = await fetch(`${API}/activity_team?employee_id=eq.${encodeURIComponent(session.id)}`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const team = await r.json();\u001b[39m\n\u001b[31m      if (team.length) {\u001b[39m\n\u001b[31m        const ids = team.map(t => t.activity_id).join(',');\u001b[39m\n\u001b[31m        const r2 = await fetch(`${API}/activities?id=in.(${ids})&active=eq.true`, { headers: ACT_RD });\u001b[39m\n\u001b[31m        acts = await r2.json();\u001b[39m\n\u001b[31m        // Attach role\u001b[39m\n\u001b[31m        acts = acts.map(a => ({ ...a, _myRole: team.find(t => t.activity_id===a.id)?.role }));\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    activities = acts;\u001b[39m\n\u001b[31m    if (!acts.length) { el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">🎯</div><p>No activities assigned to you yet.</p></div>`; return; }·\u001b[39m\n\u001b[31m    // Load response counts\u001b[39m\n\u001b[31m    const ids = acts.map(a => `\\\"${a.id}\\\"`).join(',');\u001b[39m\n\u001b[31m    let countMap = {};\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const cr = await fetch(`${API}/responses?activity_id=in.(${acts.map(a=>a.id).join(',')})&select=activity_id`, { headers: ACT_RD });\u001b[39m\n\u001b[31m      const crows = await cr.json();\u001b[39m\n\u001b[31m      crows.forEach(r => { countMap[r.activity_id] = (countMap[r.activity_id]||0)+1; });\u001b[39m\n\u001b[31m    } catch(_) {}·\u001b[39m\n\u001b[31m    const isAdminUser = ADMIN_IDS.includes(session.id);\u001b[39m\n\u001b[31m    el.innerHTML = acts.map(a => `\u001b[39m\n\u001b[31m      <div class=\\\"card\\\" onclick=\\\"openActivity('${a.id}')\\\">\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:8px;\\\">\u001b[39m\n\u001b[31m          <div class=\\\"card-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m          <div style=\\\"display:flex;align-items:center;gap:8px;flex-shrink:0\\\">\u001b[39m\n\u001b[31m            <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m            ${isAdminUser ? `<button class=\\\"icon-btn\\\" style=\\\"font-size:1rem;padding:2px 6px\\\" onclick=\\\"event.stopPropagation();directManage('${a.id}')\\\" title=\\\"Manage Access\\\">👥</button>` : ''}\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"card-meta\\\">\u001b[39m\n\u001b[31m          ${a.city ? `<span>📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m          ${a.start_date ? `<span>📅 ${fmtDateRange(a.start_date, a.end_date)}</span>` : ''}\u001b[39m\n\u001b[31m          <span>📋 ${countMap[a.id]||0} response${(countMap[a.id]||0)!==1?'s':''}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `).join('');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load activities.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openActivity(id) {\u001b[39m\n\u001b[31m  currentActivity = activities.find(a => a.id === id);\u001b[39m\n\u001b[31m  if (!currentActivity) return;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  buildActivityDetail();\u001b[39m\n\u001b[31m  showActivity();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildActivityDetail() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const canView = true;·\u001b[39m\n\u001b[31m  document.getElementById('act-topbar-title').textContent = a.title.length > 24 ? a.title.slice(0,24)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('act-manage-btn').style.display = isAdmin ? '' : 'none';·\u001b[39m\n\u001b[31m  document.getElementById('act-header').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"act-title\\\">${esc(a.title)}</div>\u001b[39m\n\u001b[31m    <div class=\\\"act-meta-row\\\">\u001b[39m\n\u001b[31m      <span class=\\\"badge badge-teal\\\">${esc(a.company.toUpperCase())}</span>\u001b[39m\n\u001b[31m      ${a.city ? `<span class=\\\"badge badge-blue\\\">📍 ${esc(a.city)}</span>` : ''}\u001b[39m\n\u001b[31m      ${myRole ? `<span class=\\\"badge badge-green\\\">${esc(myRole)}</span>` : ''}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${a.start_date ? `<div style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;\\\">📅 ${fmtDateRange(a.start_date, a.end_date)}</div>` : ''}\u001b[39m\n\u001b[31m  `;·\u001b[39m\n\u001b[31m  const btns = document.getElementById('act-action-btns');\u001b[39m\n\u001b[31m  btns.innerHTML = '';\u001b[39m\n\u001b[31m  if (canCapture) {\u001b[39m\n\u001b[31m    const b1 = document.createElement('button');\u001b[39m\n\u001b[31m    b1.className = 'btn-primary'; b1.textContent = '📝 Capture Response';\u001b[39m\n\u001b[31m    b1.onclick = () => { photos = []; editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingArea = ''; pendingLocation = ''; showAreaConfirm(); };\u001b[39m\n\u001b[31m    btns.appendChild(b1);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (canView) {\u001b[39m\n\u001b[31m    const b2 = document.createElement('button');\u001b[39m\n\u001b[31m    b2.className = 'btn-sec'; b2.textContent = '📋 View Responses';\u001b[39m\n\u001b[31m    b2.onclick = showResponses;\u001b[39m\n\u001b[31m    btns.appendChild(b2);\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction getMyRole(a) {\u001b[39m\n\u001b[31m  if (session.type === 'client') return session.role;\u001b[39m\n\u001b[31m  if (ADMIN_IDS.includes(session.id)) return 'admin';\u001b[39m\n\u001b[31m  return a._myRole || 'view';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Capture Form ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildCaptureForm() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const isDraft = !!editingDraftId;\u001b[39m\n\u001b[31m  document.getElementById('capture-title').textContent = isDraft ? 'Continue Draft' : 'Capture Response';\u001b[39m\n\u001b[31m  document.getElementById('cap-sub').textContent = a.title.length > 18 ? a.title.slice(0,18)+'…' : a.title;\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').textContent = isDraft ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  document.getElementById('submit-btn').disabled = false;\u001b[39m\n\u001b[31m  const isClient = session?.type === 'client';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').style.display = isClient ? 'none' : '';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  document.getElementById('draft-btn').disabled = false;\u001b[39m\n\u001b[31m  document.getElementById('del-draft-btn').style.display = (!isClient && isDraft) ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10);\u001b[39m\n\u001b[31m  const draftFd = isDraft ? (currentResponse?.form_data || {}) : {};·\u001b[39m\n\u001b[31m  let html = pendingArea\u001b[39m\n\u001b[31m    ? `<div style=\\\"margin-bottom:14px;padding:8px 12px;background:rgba(13,148,136,.1);border-radius:8px;font-size:.85rem;color:var(--teal3)\\\">📍 <strong>${esc(pendingArea)}</strong></div>`\u001b[39m\n\u001b[31m    : '';\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    const locCoordsHtml = pendingLocation\u001b[39m\n\u001b[31m      ? `<div class=\\\"loc-coords\\\">${esc(pendingLocation)}</div>`\u001b[39m\n\u001b[31m      : `<div style=\\\"color:var(--muted);font-size:.76rem\\\">Not captured yet</div>`;\u001b[39m\n\u001b[31m    html += `<div class=\\\"loc-row\\\" id=\\\"loc-row\\\">\u001b[39m\n\u001b[31m      <div class=\\\"loc-info\\\"><div>📍 Your Location</div>${locCoordsHtml}</div>\u001b[39m\n\u001b[31m      <button class=\\\"loc-capture-btn\\\" onclick=\\\"captureLocation()\\\">${pendingLocation ? '🔄 Re-capture' : 'Capture Location'}</button>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const req = f.required ? `<span class=\\\"req\\\">*</span>` : '';\u001b[39m\n\u001b[31m    const val = esc(draftFd[f.key] || '');\u001b[39m\n\u001b[31m    if (f.type === 'select') {\u001b[39m\n\u001b[31m      const opts = (f.options || []).map(o => `<option value=\\\"${esc(o)}\\\"${draftFd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <select class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" ${f.required?'required':''}>\u001b[39m\n\u001b[31m          <option value=\\\"\\\">Select…</option>${opts}\u001b[39m\n\u001b[31m        </select>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <textarea class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" ${f.required?'required':''}>${val}</textarea>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"form-group\\\">\u001b[39m\n\u001b[31m        <div class=\\\"form-label\\\">${esc(f.label)} ${req}</div>\u001b[39m\n\u001b[31m        <input type=\\\"${f.type||'text'}\\\" class=\\\"form-input\\\" id=\\\"f_${f.key}\\\" placeholder=\\\"${esc(f.label)}\\\" value=\\\"${val}\\\" ${f.required?'required':''}\u001b[39m\n\u001b[31m          ${f.type==='tel'?'inputmode=\\\"tel\\\"':''} ${f.type==='email'?'inputmode=\\\"email\\\"':''}>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  // Photo section\u001b[39m\n\u001b[31m  html += `<div class=\\\"photo-section\\\">\u001b[39m\n\u001b[31m    <div class=\\\"photo-section-hdr\\\">\u001b[39m\n\u001b[31m      <h3>📸 New Photos (up to ${MAX_PHOTOS})</h3>\u001b[39m\n\u001b[31m      <div class=\\\"photo-quality-badge\\\" onclick=\\\"openSettings()\\\" id=\\\"qual-badge\\\">Quality: ${quality}%</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    <div class=\\\"photo-grid\\\" id=\\\"photo-grid\\\"></div>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  document.getElementById('capture-form-body').innerHTML = html;\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  renderPhotoGrid();\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoGrid() {\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-grid');\u001b[39m\n\u001b[31m  if (!grid) return;\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  for (let i = 0; i < MAX_PHOTOS; i++) {\u001b[39m\n\u001b[31m    if (i < photos.length) {\u001b[39m\n\u001b[31m      const p = photos[i];\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot has-photo\\\" onclick=\\\"photoSlotClick(${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" alt=\\\"\\\">\u001b[39m\n\u001b[31m        <div class=\\\"photo-overlay\\\"></div>\u001b[39m\n\u001b[31m        <div class=\\\"hd-btn ${p.hdMode?'on':''}\\\" onclick=\\\"event.stopPropagation();toggleHD(${i})\\\">${p.hdMode?'HD':'C'}</div>\u001b[39m\n\u001b[31m        <div class=\\\"rm-btn\\\" onclick=\\\"event.stopPropagation();removePhoto(${i})\\\">✕</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      html += `<div class=\\\"photo-slot\\\" onclick=\\\"addPhotoSlot(${i})\\\">\u001b[39m\n\u001b[31m        <div class=\\\"add-icon\\\">+</div>\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  grid.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderDraftExistingPhotos() {\u001b[39m\n\u001b[31m  const container = document.getElementById('draft-existing-photos');\u001b[39m\n\u001b[31m  if (!container) return;\u001b[39m\n\u001b[31m  if (!editingDraftId || !draftExistingPhotos.length) { container.style.display = 'none'; return; }\u001b[39m\n\u001b[31m  container.style.display = '';\u001b[39m\n\u001b[31m  container.innerHTML = `\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.8rem;font-weight:600;color:var(--muted);margin-bottom:8px\\\">Uploaded Photos (${draftExistingPhotos.length})</div>\u001b[39m\n\u001b[31m    <div style=\\\"display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px\\\">\u001b[39m\n\u001b[31m      ${draftExistingPhotos.map(p => `\u001b[39m\n\u001b[31m        <div style=\\\"position:relative;width:72px;height:72px;border-radius:8px;overflow:hidden;opacity:${draftDeletedPhotoIds.includes(p.id)?0.3:1};transition:.2s\\\">\u001b[39m\n\u001b[31m          <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m          <button onclick=\\\"toggleDeleteDraftPhoto(${p.id})\\\"\u001b[39m\n\u001b[31m            style=\\\"position:absolute;top:2px;right:2px;background:${draftDeletedPhotoIds.includes(p.id)?'rgba(99,102,241,.8)':'rgba(239,68,68,.8)'};color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">\u001b[39m\n\u001b[31m            ${draftDeletedPhotoIds.includes(p.id)? '↩':'✕'}\u001b[39m\n\u001b[31m          </button>\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDeleteDraftPhoto(id) {\u001b[39m\n\u001b[31m  const idx = draftDeletedPhotoIds.indexOf(id);\u001b[39m\n\u001b[31m  if (idx >= 0) draftDeletedPhotoIds.splice(idx, 1);\u001b[39m\n\u001b[31m  else draftDeletedPhotoIds.push(id);\u001b[39m\n\u001b[31m  renderDraftExistingPhotos();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openDraft(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  currentResponse = r;\u001b[39m\n\u001b[31m  editingDraftId = id;\u001b[39m\n\u001b[31m  pendingArea = r.area || '';\u001b[39m\n\u001b[31m  pendingLocation = r.location || '';\u001b[39m\n\u001b[31m  draftExistingPhotos = r._photos || [];\u001b[39m\n\u001b[31m  draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m  photos = [];\u001b[39m\n\u001b[31m  show('scr-capture');\u001b[39m\n\u001b[31m  buildCaptureForm();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveDraft() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('draft-btn');\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    formData[f.key] = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', editingDraftId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: editingDraftId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + newRows.length;\u001b[39m\n\u001b[31m      await fetch(`${API}/responses?id=eq.${editingDraftId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: formData, photo_count: keptCount, area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: 0, status: 'draft', area: pendingArea || null, location: pendingLocation || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) { const detail = await rr.text().catch(() => ''); throw new Error(`HTTP ${rr.status}${detail ? ' — ' + detail.slice(0,200) : ''}`); }\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      const rid = saved.id;\u001b[39m\n\u001b[31m      const photoRows = []; let uploaded = 0;\u001b[39m\n\u001b[31m      for (const p of photos) {\u001b[39m\n\u001b[31m        try {\u001b[39m\n\u001b[31m          const fd2 = new FormData();\u001b[39m\n\u001b[31m          fd2.append('response_id', rid); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m          fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m          const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m          if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: rid, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploaded++; }\u001b[39m\n\u001b[31m        } catch(_) {}\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      if (photoRows.length) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m        await fetch(`${API}/responses?id=eq.${rid}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploaded }) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    toast('Draft saved.', 'ok');\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';\u001b[39m\n\u001b[31m    setTimeout(() => showResponses(), 1500);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('saveDraft error:', e);\u001b[39m\n\u001b[31m    toast(`Draft save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = '💾 Save as Draft';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet pendingSlot = null;\u001b[39m\n\u001b[31mlet pendingPhotoCallback = null;·\u001b[39m\n\u001b[31mfunction openPhotoSrc(cb) {\u001b[39m\n\u001b[31m  pendingPhotoCallback = cb;\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closePhotoSrc() {\u001b[39m\n\u001b[31m  document.getElementById('photo-src-modal').classList.remove('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction cancelPhotoSrc() {\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickCamera() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-camera-input').click();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction pickGallery() {\u001b[39m\n\u001b[31m  closePhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').value = '';\u001b[39m\n\u001b[31m  document.getElementById('photo-gallery-input').click();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addPhotoSlot(idx) {\u001b[39m\n\u001b[31m  pendingSlot = idx;\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    const entry = { file, hdMode: false, preview, blob };\u001b[39m\n\u001b[31m    photos.splice(pendingSlot, 0, entry);\u001b[39m\n\u001b[31m    photos = photos.slice(0, MAX_PHOTOS);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function onPhotoChosen(e) {\u001b[39m\n\u001b[31m  const file = e.target.files[0];\u001b[39m\n\u001b[31m  if (!file || !pendingPhotoCallback) return;\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const blob = await compressImage(file, quality, false);\u001b[39m\n\u001b[31m  const preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m  const cb = pendingPhotoCallback;\u001b[39m\n\u001b[31m  pendingPhotoCallback = null;\u001b[39m\n\u001b[31m  cb({ blob, preview, file });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleHD(idx) {\u001b[39m\n\u001b[31m  const quality = parseInt(localStorage.getItem(QUAL_KEY) || '70', 10) / 100;\u001b[39m\n\u001b[31m  const p = photos[idx];\u001b[39m\n\u001b[31m  p.hdMode = !p.hdMode;\u001b[39m\n\u001b[31m  compressImage(p.file, quality, p.hdMode).then(blob => {\u001b[39m\n\u001b[31m    p.blob = blob;\u001b[39m\n\u001b[31m    p.preview = URL.createObjectURL(blob);\u001b[39m\n\u001b[31m    renderPhotoGrid();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removePhoto(idx) { photos.splice(idx,1); renderPhotoGrid(); }\u001b[39m\n\u001b[31mfunction photoSlotClick(idx) {\u001b[39m\n\u001b[31m  // On tap of an existing photo — open lightbox with current photo\u001b[39m\n\u001b[31m  openLightbox(photos.map(p=>p.preview), idx);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function compressImage(file, quality, hd) {\u001b[39m\n\u001b[31m  if (hd) return file;\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    const img = new Image();\u001b[39m\n\u001b[31m    const url = URL.createObjectURL(file);\u001b[39m\n\u001b[31m    img.onload = () => {\u001b[39m\n\u001b[31m      URL.revokeObjectURL(url);\u001b[39m\n\u001b[31m      const MAX = 1600;\u001b[39m\n\u001b[31m      let w = img.width, h = img.height;\u001b[39m\n\u001b[31m      if (w > MAX || h > MAX) {\u001b[39m\n\u001b[31m        if (w > h) { h = Math.round(h * MAX/w); w = MAX; }\u001b[39m\n\u001b[31m        else { w = Math.round(w * MAX/h); h = MAX; }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const canvas = document.createElement('canvas');\u001b[39m\n\u001b[31m      canvas.width = w; canvas.height = h;\u001b[39m\n\u001b[31m      canvas.getContext('2d').drawImage(img, 0, 0, w, h);\u001b[39m\n\u001b[31m      canvas.toBlob(blob => resolve(blob || file), 'image/jpeg', quality);\u001b[39m\n\u001b[31m    };\u001b[39m\n\u001b[31m    img.onerror = () => { URL.revokeObjectURL(url); resolve(file); };\u001b[39m\n\u001b[31m    img.src = url;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Submit Response ────────────────────────────────────── */\u001b[39m\n\u001b[31m/* ─── Submission datetime picker ─────────────────────── */\u001b[39m\n\u001b[31mlet _submitDtResolve = null;\u001b[39m\n\u001b[31mlet _submitDtMin = null; // draft_at as UTC ISO — used for client-side lower-bound validation·\u001b[39m\n\u001b[31mfunction pickSubmissionDatetime(draftAt) {\u001b[39m\n\u001b[31m  return new Promise(resolve => {\u001b[39m\n\u001b[31m    _submitDtResolve = resolve;\u001b[39m\n\u001b[31m    _submitDtMin = draftAt || null;\u001b[39m\n\u001b[31m    const minDt = draftAt ? ceilMinISTDatetimeLocal(draftAt) : null;\u001b[39m\n\u001b[31m    const maxDt = nowISTDatetimeLocal();\u001b[39m\n\u001b[31m    const input = document.getElementById('submit-dt-input');\u001b[39m\n\u001b[31m    input.value = maxDt;\u001b[39m\n\u001b[31m    input.max = maxDt;\u001b[39m\n\u001b[31m    if (minDt) input.min = minDt; else input.removeAttribute('min');\u001b[39m\n\u001b[31m    const info = document.getElementById('submit-dt-draft-info');\u001b[39m\n\u001b[31m    info.textContent = draftAt ? `Draft saved: ${fmtISTDatetime(draftAt)}` : '';\u001b[39m\n\u001b[31m    const hint = document.getElementById('submit-dt-hint');\u001b[39m\n\u001b[31m    const minHuman = draftAt ? new Date(draftAt).toLocaleString('en-IN', {\u001b[39m\n\u001b[31m      day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata'\u001b[39m\n\u001b[31m    }) + ' IST' : null;\u001b[39m\n\u001b[31m    hint.textContent = minHuman\u001b[39m\n\u001b[31m      ? `Min: ${minHuman} (when draft was saved)  ·  Max: now`\u001b[39m\n\u001b[31m      : `Max: now (IST)`;\u001b[39m\n\u001b[31m    document.getElementById('submit-dt-modal').classList.add('open');\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction cancelSubmitDatetime() {\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(null); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmSubmitDatetime() {\u001b[39m\n\u001b[31m  const val = document.getElementById('submit-dt-input').value;\u001b[39m\n\u001b[31m  if (!val) { toast('Please select a date and time', 'err'); return; }\u001b[39m\n\u001b[31m  const chosen = istInputToISO(val);\u001b[39m\n\u001b[31m  if (new Date(chosen) > new Date(Date.now() + 5 * 60000)) {\u001b[39m\n\u001b[31m    toast('Submission time cannot be in the future', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (_submitDtMin && new Date(chosen) < new Date(_submitDtMin)) {\u001b[39m\n\u001b[31m    toast('Submission date cannot be before the draft was saved', 'err'); return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('submit-dt-modal').classList.remove('open');\u001b[39m\n\u001b[31m  if (_submitDtResolve) { _submitDtResolve(chosen); _submitDtResolve = null; }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function submitResponse() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const btn = document.getElementById('submit-btn');·\u001b[39m\n\u001b[31m  const formData = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`f_${f.key}`);\u001b[39m\n\u001b[31m    const val = el ? el.value.trim() : '';\u001b[39m\n\u001b[31m    if (f.required && !val) { toast(`Required: ${f.label}`, 'err'); el?.focus(); return; }\u001b[39m\n\u001b[31m    formData[f.key] = val;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Draft finalization: show datetime picker before proceeding\u001b[39m\n\u001b[31m  let chosenSubmittedAt = null;\u001b[39m\n\u001b[31m  if (editingDraftId) {\u001b[39m\n\u001b[31m    chosenSubmittedAt = await pickSubmissionDatetime(currentResponse?.draft_at);\u001b[39m\n\u001b[31m    if (!chosenSubmittedAt) return; // user cancelled\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gpsLoc = pendingLocation || null;\u001b[39m\n\u001b[31m    btn.innerHTML = `<span class=\\\"spinner\\\"></span> Saving…`;\u001b[39m\n\u001b[31m    let responseId, uploadedCount = 0;·\u001b[39m\n\u001b[31m    if (editingDraftId) {\u001b[39m\n\u001b[31m      // Finalizing an existing draft\u001b[39m\n\u001b[31m      responseId = editingDraftId;\u001b[39m\n\u001b[31m      for (const pid of draftDeletedPhotoIds) {\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos?id=eq.${pid}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const newRows = [];\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); newRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (newRows.length) await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(newRows) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m      const keptCount = draftExistingPhotos.filter(p => !draftDeletedPhotoIds.includes(p.id)).length + uploadedCount;\u001b[39m\n\u001b[31m      const draftPatch = { form_data: formData, status: 'submitted', location: gpsLoc, photo_count: keptCount, area: pendingArea || null };\u001b[39m\n\u001b[31m      if (chosenSubmittedAt) draftPatch.submitted_at = chosenSubmittedAt;\u001b[39m\n\u001b[31m      const pr = await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify(draftPatch) });\u001b[39m\n\u001b[31m      if (!pr.ok) {\u001b[39m\n\u001b[31m        const raw = await pr.text().catch(() => '');\u001b[39m\n\u001b[31m        let msg = `Save failed (${pr.status})`;\u001b[39m\n\u001b[31m        try { const j = JSON.parse(raw); msg = j.message || msg; } catch(_) {}\u001b[39m\n\u001b[31m        throw new Error(msg);\u001b[39m\n\u001b[31m      }·\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      // New response\u001b[39m\n\u001b[31m      const capturedBy = session.type === 'client' ? `client:${session.clientAccessId}` : session.id;\u001b[39m\n\u001b[31m      const rr = await fetch(`${API}/responses`, { method:'POST', headers: ACT_H,\u001b[39m\n\u001b[31m        body: JSON.stringify({ activity_id: a.id, captured_by: capturedBy, form_data: formData, photo_count: photos.length, status: 'submitted', location: gpsLoc, area: pendingArea || null }) });\u001b[39m\n\u001b[31m      if (!rr.ok) throw new Error(`Save failed: ${rr.status}`);\u001b[39m\n\u001b[31m      const [saved] = await rr.json();\u001b[39m\n\u001b[31m      responseId = saved.id;·\u001b[39m\n\u001b[31m      if (photos.length > 0) {\u001b[39m\n\u001b[31m        btn.textContent = 'Uploading photos…';\u001b[39m\n\u001b[31m        const photoRows = [];\u001b[39m\n\u001b[31m        for (const p of photos) {\u001b[39m\n\u001b[31m          try {\u001b[39m\n\u001b[31m            const fd2 = new FormData();\u001b[39m\n\u001b[31m            fd2.append('response_id', responseId); fd2.append('activity_id', a.id);\u001b[39m\n\u001b[31m            fd2.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m            const ur = await fetch(`${PROXY}/upload-activity`, { method:'POST', body: fd2 });\u001b[39m\n\u001b[31m            if (ur.ok) { const ud = await ur.json(); photoRows.push({ response_id: responseId, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes }); uploadedCount++; }\u001b[39m\n\u001b[31m          } catch(_) {}\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m        if (photoRows.length) {\u001b[39m\n\u001b[31m          await fetch(`${API}/response_photos`, { method:'POST', headers: ACT_H, body: JSON.stringify(photoRows) });\u001b[39m\n\u001b[31m          await fetch(`${API}/responses?id=eq.${responseId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ photo_count: uploadedCount }) });\u001b[39m\n\u001b[31m        }\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }·\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = []; pendingLocation = '';·\u001b[39m\n\u001b[31m    if (photos.length > 0 && uploadedCount === 0) {\u001b[39m\n\u001b[31m      toast(`Response saved, but photos failed to upload. Open the response and tap 📸 to retry.`, 'err');\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      toast(`Saved! ${uploadedCount} photo${uploadedCount!==1?'s':''} uploaded.`, 'ok');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    setTimeout(() => { showActivity(); }, 2000);·\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    console.error('submitResponse error:', e);\u001b[39m\n\u001b[31m    toast(`Save failed: ${e.message || 'network error'}`, 'err');\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.textContent = editingDraftId ? '✅ Submit Response' : 'Submit Response';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Responses List ──────────────────────────────────── */\u001b[39m\n\u001b[31mlet activeAreaFilters       = new Set();\u001b[39m\n\u001b[31mlet activeMonthFilters      = new Set();\u001b[39m\n\u001b[31mlet activeDateFilters       = new Set();\u001b[39m\n\u001b[31mlet activeLeadTypeFilters   = new Set();\u001b[39m\n\u001b[31mlet activeSubmissionFilters = new Set();\u001b[39m\n\u001b[31mlet filterSeqCounter = 0;\u001b[39m\n\u001b[31mlet filterSeqMap     = {};·\u001b[39m\n\u001b[31m/* ─── Cascading filter helpers ─────────────────────────── */\u001b[39m\n\u001b[31mfunction matchesFiltersExcept(r, skip) {\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m  if (!skip.area && activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m  if (!skip.month && activeMonthFilters.size) {\u001b[39m\n\u001b[31m    const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m    const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m    if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.date && activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10)||'')) return false;\u001b[39m\n\u001b[31m  if (!skip.leadType && activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m    if (isDraft || !activeLeadTypeFilters.has(fd.lead_type||'')) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  if (!skip.submission && activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m    let m = false;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('draft') && isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('submitted') && !isDraft) m = true;\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.has('gform') && r.gform_submitted) m = true;\u001b[39m\n\u001b[31m    if (!m) return false;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  return true;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction computeAvailable() {\u001b[39m\n\u001b[31m  const areas = new Set(), months = new Set(), dates = new Set(), leadTypes = new Set(), submissions = new Set();\u001b[39m\n\u001b[31m  currentResponses.forEach(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {area:true}))       { if (r.area) areas.add(r.area); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {month:true}))      {\u001b[39m\n\u001b[31m      if (r.submitted_at) {\u001b[39m\n\u001b[31m        const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m        months.add(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`);\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {date:true}))       { if (r.submitted_at) dates.add(r.submitted_at.slice(0,10)); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {leadType:true}))   { if (!isDraft && fd.lead_type) leadTypes.add(fd.lead_type); }\u001b[39m\n\u001b[31m    if (matchesFiltersExcept(r, {submission:true})) {\u001b[39m\n\u001b[31m      if (isDraft) submissions.add('draft');\u001b[39m\n\u001b[31m      else { submissions.add('submitted'); if (r.gform_submitted) submissions.add('gform'); }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  return { areas, months, dates, leadTypes, submissions };\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Stats Panel ──────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toggleStatsPanel() {\u001b[39m\n\u001b[31m  statsVisible = !statsVisible;\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  if (statsVisible) { panel.style.display = ''; buildStatsPanel(); }\u001b[39m\n\u001b[31m  else panel.style.display = 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction buildStatsPanel() {\u001b[39m\n\u001b[31m  const panel = document.getElementById('resp-stats-panel');\u001b[39m\n\u001b[31m  if (!panel) return;\u001b[39m\n\u001b[31m  const total = currentResponses.length;\u001b[39m\n\u001b[31m  if (!total) { panel.innerHTML = `<div style=\\\"color:var(--muted);font-size:.85rem;padding:4px 0\\\">No responses loaded.</div>`; return; }·\u001b[39m\n\u001b[31m  const isFiltered = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  const src = isFiltered ? filteredResponses : currentResponses;\u001b[39m\n\u001b[31m  const srcCount = src.length;·\u001b[39m\n\u001b[31m  const leads = { Hot:0, Warm:0, Cold:0 };\u001b[39m\n\u001b[31m  const areaCounts = {}, monthCounts = {}, dateCounts = {};\u001b[39m\n\u001b[31m  src.forEach(r => {\u001b[39m\n\u001b[31m    const lt = r.form_data?.lead_type || '';\u001b[39m\n\u001b[31m    if (lt in leads) leads[lt]++;\u001b[39m\n\u001b[31m    const area = r.area || 'Unknown';\u001b[39m\n\u001b[31m    areaCounts[area] = (areaCounts[area]||0)+1;\u001b[39m\n\u001b[31m    if (r.submitted_at) {\u001b[39m\n\u001b[31m      const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m      const mk = d.toLocaleDateString('en-IN',{month:'short',year:'2-digit'});\u001b[39m\n\u001b[31m      monthCounts[mk] = (monthCounts[mk]||0)+1;\u001b[39m\n\u001b[31m      const dk = r.submitted_at.slice(0,10);\u001b[39m\n\u001b[31m      dateCounts[dk] = (dateCounts[dk]||0)+1;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  const bar = (n, max, color) => {\u001b[39m\n\u001b[31m    const pct = max ? Math.round(n/max*100) : 0;\u001b[39m\n\u001b[31m    return `<div class=\\\"stat-bar-wrap\\\"><div class=\\\"stat-bar\\\" style=\\\"width:${pct}%;background:${color}\\\"></div></div>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const titleLine = isFiltered\u001b[39m\n\u001b[31m    ? `Filtered: ${srcCount} / Total: ${total}`\u001b[39m\n\u001b[31m    : `All responses · ${total}`;·\u001b[39m\n\u001b[31m  let html = `<div style=\\\"display:flex;align-items:center;justify-content:space-between;margin-bottom:10px\\\">\u001b[39m\n\u001b[31m    <div style=\\\"font-size:.72rem;font-weight:600;color:var(--text)\\\">${titleLine}</div>\u001b[39m\n\u001b[31m    <button onclick=\\\"toggleStatsPanel()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:1rem;cursor:pointer;padding:2px 6px\\\">✕</button>\u001b[39m\n\u001b[31m  </div>`;·\u001b[39m\n\u001b[31m  // Lead type\u001b[39m\n\u001b[31m  const maxL = Math.max(...Object.values(leads), 1);\u001b[39m\n\u001b[31m  html += `<div class=\\\"stats-panel-title\\\">Lead Type</div><div class=\\\"stat-cols\\\" style=\\\"margin-bottom:12px\\\">`;\u001b[39m\n\u001b[31m  [['Hot','var(--hot)'],['Warm','var(--warm)'],['Cold','var(--cold)']].forEach(([k,c]) => {\u001b[39m\n\u001b[31m    html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${k}</span><span style=\\\"color:${c};font-weight:700\\\">${leads[k]}</span></div>${bar(leads[k],maxL,c)}</div>`;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  html += `</div>`;·\u001b[39m\n\u001b[31m  // By Area\u001b[39m\n\u001b[31m  const sortedAreas = Object.entries(areaCounts).sort((a,b)=>b[1]-a[1]);\u001b[39m\n\u001b[31m  if (sortedAreas.length) {\u001b[39m\n\u001b[31m    const maxA = sortedAreas[0][1];\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Area</div>`;\u001b[39m\n\u001b[31m    sortedAreas.forEach(([a,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(a)}</span><span>${n}</span></div>${bar(n,maxA,'var(--teal3)')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Month\u001b[39m\n\u001b[31m  const sortedMonths = Object.entries(monthCounts);\u001b[39m\n\u001b[31m  if (sortedMonths.length) {\u001b[39m\n\u001b[31m    const maxM = Math.max(...sortedMonths.map(e=>e[1]));\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Month</div>`;\u001b[39m\n\u001b[31m    sortedMonths.forEach(([m,n]) => {\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${esc(m)}</span><span>${n}</span></div>${bar(n,maxM,'#818cf8')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    html += `<div style=\\\"height:10px\\\"></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // By Date (most recent 14)\u001b[39m\n\u001b[31m  const sortedDates = Object.entries(dateCounts).sort((a,b)=>b[0].localeCompare(a[0])).slice(0,14);\u001b[39m\n\u001b[31m  if (sortedDates.length) {\u001b[39m\n\u001b[31m    const maxD = sortedDates.reduce((m,[,n])=>Math.max(m,n),1);\u001b[39m\n\u001b[31m    html += `<div class=\\\"stats-panel-title\\\">By Date (recent)</div>`;\u001b[39m\n\u001b[31m    sortedDates.forEach(([d,n]) => {\u001b[39m\n\u001b[31m      const disp = new Date(d+'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      html += `<div class=\\\"stat-row\\\"><div class=\\\"stat-row-label\\\"><span>${disp}</span><span>${n}</span></div>${bar(n,maxD,'#34d399')}</div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  panel.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function loadResponses() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const clientFilter = session?.type === 'client' ? '&gform_submitted=eq.true' : '';\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/responses?activity_id=eq.${encodeURIComponent(currentActivity.id)}${clientFilter}&order=submitted_at.desc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    currentResponses = await r.json();\u001b[39m\n\u001b[31m    sub.textContent = `${currentResponses.length} total`;\u001b[39m\n\u001b[31m    if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m    renderFilterRows();\u001b[39m\n\u001b[31m    filterResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderFilterRows() {\u001b[39m\n\u001b[31m  const container = document.getElementById('filter-rows-container');\u001b[39m\n\u001b[31m  if (!container) return;·\u001b[39m\n\u001b[31m  const avail = computeAvailable();·\u001b[39m\n\u001b[31m  const allAreas    = [...new Set(currentResponses.map(r => r.area).filter(Boolean))].sort();\u001b[39m\n\u001b[31m  const allMonths   = [...new Set(currentResponses.map(r => {\u001b[39m\n\u001b[31m    if (!r.submitted_at) return null;\u001b[39m\n\u001b[31m    const d = new Date(r.submitted_at);\u001b[39m\n\u001b[31m    return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}`;\u001b[39m\n\u001b[31m  }).filter(Boolean))].sort().reverse();\u001b[39m\n\u001b[31m  const allDates    = [...new Set(currentResponses.map(r => r.submitted_at?.slice(0,10)).filter(Boolean))].sort().reverse();·\u001b[39m\n\u001b[31m  const leadTypeDefs = [\u001b[39m\n\u001b[31m    { key:'Hot',  label:'🔴 Hot' },\u001b[39m\n\u001b[31m    { key:'Warm', label:'🟡 Warm' },\u001b[39m\n\u001b[31m    { key:'Cold', label:'🔵 Cold' },\u001b[39m\n\u001b[31m  ];\u001b[39m\n\u001b[31m  const submissionDefs = [\u001b[39m\n\u001b[31m    { key:'draft',     label:'📝 Draft' },\u001b[39m\n\u001b[31m    { key:'submitted', label:'✅ Submitted' },\u001b[39m\n\u001b[31m    { key:'gform',     label:'📋 GForm' },\u001b[39m\n\u001b[31m  ];·\u001b[39m\n\u001b[31m  const chip = (label, activeSet, key, dim, onclick, availSet) => {\u001b[39m\n\u001b[31m    const isActive   = activeSet.has(key);\u001b[39m\n\u001b[31m    const inAvail    = availSet.has(key);\u001b[39m\n\u001b[31m    const isConflict = isActive && !inAvail;\u001b[39m\n\u001b[31m    let cls = 'filter-chip';\u001b[39m\n\u001b[31m    if (isConflict)    cls += ' conflict';\u001b[39m\n\u001b[31m    else if (isActive) cls += ' active';\u001b[39m\n\u001b[31m    else if (!inAvail) cls += ' unavailable';\u001b[39m\n\u001b[31m    const sk = `${dim}:${key}`;\u001b[39m\n\u001b[31m    const seq = filterSeqMap[sk];\u001b[39m\n\u001b[31m    const badge = seq ? `<span class=\\\"seq-badge\\\">${seq}</span>` : '';\u001b[39m\n\u001b[31m    return `<button class=\\\"${cls}\\\" onclick=\\\"${onclick}\\\">${label}${badge}</button>`;\u001b[39m\n\u001b[31m  };·\u001b[39m\n\u001b[31m  const anyFilter = activeAreaFilters.size || activeMonthFilters.size || activeDateFilters.size || activeLeadTypeFilters.size || activeSubmissionFilters.size;\u001b[39m\n\u001b[31m  let html = anyFilter\u001b[39m\n\u001b[31m    ? `<div style=\\\"display:flex;justify-content:flex-end;margin-bottom:6px\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"clearAllFilters()\\\" style=\\\"padding:4px 12px;border-radius:20px;border:1px solid rgba(239,68,68,.35);background:rgba(239,68,68,.1);color:var(--red);font-size:.78rem;cursor:pointer\\\">✕ Clear Filters</button>\u001b[39m\n\u001b[31m      </div>`\u001b[39m\n\u001b[31m    : '';·\u001b[39m\n\u001b[31m  if (allAreas.length > 0) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Area</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeAreaFilters.size?' active':''}\\\" onclick=\\\"clearAreaFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allAreas.map(a => chip(esc(a), activeAreaFilters, a, 'area', `toggleAreaFilter('${esc(a)}')`, avail.areas)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allMonths.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Month</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeMonthFilters.size?' active':''}\\\" onclick=\\\"clearMonthFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allMonths.map(k => {\u001b[39m\n\u001b[31m      const d = new Date(k + '-01');\u001b[39m\n\u001b[31m      const lbl = d.toLocaleDateString('en-IN',{month:'short'}) + '-' + String(d.getFullYear()).slice(-2);\u001b[39m\n\u001b[31m      return chip(lbl, activeMonthFilters, k, 'month', `toggleMonthFilter('${k}')`, avail.months);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  if (allDates.length > 1) {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Date</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeDateFilters.size?' active':''}\\\" onclick=\\\"clearDateFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += allDates.map(d => {\u001b[39m\n\u001b[31m      const disp = new Date(d + 'T00:00:00').toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m      return chip(disp, activeDateFilters, d, 'date', `toggleDateFilter('${d}')`, avail.dates);\u001b[39m\n\u001b[31m    }).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Lead Type</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m  html += `<button class=\\\"filter-chip${!activeLeadTypeFilters.size?' active':''}\\\" onclick=\\\"clearLeadTypeFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m  html += leadTypeDefs.map(s => chip(s.label, activeLeadTypeFilters, s.key, 'leadType', `toggleLeadTypeFilter('${s.key}')`, avail.leadTypes)).join('');\u001b[39m\n\u001b[31m  html += `</div></div>`;·\u001b[39m\n\u001b[31m  if (session?.type !== 'client') {\u001b[39m\n\u001b[31m    html += `<div class=\\\"filter-section\\\"><div class=\\\"filter-label\\\">Submission</div><div class=\\\"filter-row\\\">`;\u001b[39m\n\u001b[31m    html += `<button class=\\\"filter-chip${!activeSubmissionFilters.size?' active':''}\\\" onclick=\\\"clearSubmissionFilter()\\\">All</button>`;\u001b[39m\n\u001b[31m    html += submissionDefs.map(s => chip(s.label, activeSubmissionFilters, s.key, 'submission', `toggleSubmissionFilter('${s.key}')`, avail.submissions)).join('');\u001b[39m\n\u001b[31m    html += `</div></div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  container.innerHTML = html;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleAreaFilter(val) {\u001b[39m\n\u001b[31m  if (activeAreaFilters.has(val)) { activeAreaFilters.delete(val); delete filterSeqMap[`area:${val}`]; }\u001b[39m\n\u001b[31m  else { activeAreaFilters.add(val); filterSeqMap[`area:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearAreaFilter() {\u001b[39m\n\u001b[31m  activeAreaFilters.forEach(v => delete filterSeqMap[`area:${v}`]);\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleMonthFilter(val) {\u001b[39m\n\u001b[31m  if (activeMonthFilters.has(val)) { activeMonthFilters.delete(val); delete filterSeqMap[`month:${val}`]; }\u001b[39m\n\u001b[31m  else { activeMonthFilters.add(val); filterSeqMap[`month:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearMonthFilter() {\u001b[39m\n\u001b[31m  activeMonthFilters.forEach(v => delete filterSeqMap[`month:${v}`]);\u001b[39m\n\u001b[31m  activeMonthFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleDateFilter(val) {\u001b[39m\n\u001b[31m  if (activeDateFilters.has(val)) { activeDateFilters.delete(val); delete filterSeqMap[`date:${val}`]; }\u001b[39m\n\u001b[31m  else { activeDateFilters.add(val); filterSeqMap[`date:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearDateFilter() {\u001b[39m\n\u001b[31m  activeDateFilters.forEach(v => delete filterSeqMap[`date:${v}`]);\u001b[39m\n\u001b[31m  activeDateFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleLeadTypeFilter(val) {\u001b[39m\n\u001b[31m  if (activeLeadTypeFilters.has(val)) { activeLeadTypeFilters.delete(val); delete filterSeqMap[`leadType:${val}`]; }\u001b[39m\n\u001b[31m  else { activeLeadTypeFilters.add(val); filterSeqMap[`leadType:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearLeadTypeFilter() {\u001b[39m\n\u001b[31m  activeLeadTypeFilters.forEach(v => delete filterSeqMap[`leadType:${v}`]);\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleSubmissionFilter(val) {\u001b[39m\n\u001b[31m  if (activeSubmissionFilters.has(val)) { activeSubmissionFilters.delete(val); delete filterSeqMap[`submission:${val}`]; }\u001b[39m\n\u001b[31m  else { activeSubmissionFilters.add(val); filterSeqMap[`submission:${val}`] = ++filterSeqCounter; }\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction clearSubmissionFilter() {\u001b[39m\n\u001b[31m  activeSubmissionFilters.forEach(v => delete filterSeqMap[`submission:${v}`]);\u001b[39m\n\u001b[31m  activeSubmissionFilters.clear(); renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction clearAllFilters() {\u001b[39m\n\u001b[31m  activeAreaFilters.clear(); activeMonthFilters.clear(); activeDateFilters.clear();\u001b[39m\n\u001b[31m  activeLeadTypeFilters.clear(); activeSubmissionFilters.clear();\u001b[39m\n\u001b[31m  filterSeqCounter = 0; filterSeqMap = {};\u001b[39m\n\u001b[31m  const s = document.getElementById('resp-search'); if (s) s.value = '';\u001b[39m\n\u001b[31m  renderFilterRows(); filterResponses();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction filterResponses() {\u001b[39m\n\u001b[31m  const q = (document.getElementById('resp-search')?.value || '').toLowerCase();\u001b[39m\n\u001b[31m  filteredResponses = currentResponses.filter(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    // text search\u001b[39m\n\u001b[31m    if (q && !Object.values(fd).join(' ').toLowerCase().includes(q)) return false;\u001b[39m\n\u001b[31m    // area filter\u001b[39m\n\u001b[31m    if (activeAreaFilters.size && !activeAreaFilters.has(r.area || '')) return false;\u001b[39m\n\u001b[31m    // month filter\u001b[39m\n\u001b[31m    if (activeMonthFilters.size) {\u001b[39m\n\u001b[31m      const d = r.submitted_at ? new Date(r.submitted_at) : null;\u001b[39m\n\u001b[31m      const mk = d ? `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}` : '';\u001b[39m\n\u001b[31m      if (!activeMonthFilters.has(mk)) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // date filter\u001b[39m\n\u001b[31m    if (activeDateFilters.size && !activeDateFilters.has(r.submitted_at?.slice(0,10) || '')) return false;\u001b[39m\n\u001b[31m    // lead type filter\u001b[39m\n\u001b[31m    if (activeLeadTypeFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      if (isDraft || !activeLeadTypeFilters.has(fd.lead_type || '')) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // submission filter\u001b[39m\n\u001b[31m    if (activeSubmissionFilters.size) {\u001b[39m\n\u001b[31m      const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m      let matched = false;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('draft') && isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('submitted') && !isDraft) matched = true;\u001b[39m\n\u001b[31m      if (activeSubmissionFilters.has('gform') && r.gform_submitted) matched = true;\u001b[39m\n\u001b[31m      if (!matched) return false;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    return true;\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  renderResponseList();\u001b[39m\n\u001b[31m  if (statsVisible) buildStatsPanel();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderResponseList() {\u001b[39m\n\u001b[31m  const el = document.getElementById('resp-list');\u001b[39m\n\u001b[31m  const sub = document.getElementById('resp-count-sub');\u001b[39m\n\u001b[31m  const total = currentResponses.length, shown = filteredResponses.length;\u001b[39m\n\u001b[31m  if (sub) sub.textContent = shown < total ? `${shown} / ${total}` : `${total} total`;\u001b[39m\n\u001b[31m  const schema = currentActivity.form_schema || [];\u001b[39m\n\u001b[31m  if (!filteredResponses.length) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">📋</div><p>No responses match your filter.</p></div>`; return;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  el.innerHTML = filteredResponses.map(r => {\u001b[39m\n\u001b[31m    const fd = r.form_data || {};\u001b[39m\n\u001b[31m    const isDraft = r.status === 'draft';\u001b[39m\n\u001b[31m    const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m    const ltBadge = !isDraft && leadType ? `<span class=\\\"badge badge-${leadType.toLowerCase()}\\\">${leadType}</span>` : '';\u001b[39m\n\u001b[31m    const draftBadge = isDraft ? `<span class=\\\"badge\\\" style=\\\"background:rgba(99,102,241,.15);color:#818cf8;font-weight:700\\\">📝 Draft</span>` : '';\u001b[39m\n\u001b[31m    const date = new Date(r.submitted_at).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m    const pcBadge = r.photo_count > 0 ? `<span style=\\\"font-size:.75rem;color:var(--muted)\\\">📸 ${r.photo_count}</span>` : '';\u001b[39m\n\u001b[31m    const gformBadge = !isDraft && currentActivity.gform_url\u001b[39m\n\u001b[31m      ? (r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge badge-green\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : `<span class=\\\"badge badge-amber\\\">⚠ GForm</span>`)\u001b[39m\n\u001b[31m      : '';\u001b[39m\n\u001b[31m    const locBadge = r.location ? `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(r.location)}\\\" target=\\\"_blank\\\" onclick=\\\"event.stopPropagation()\\\" style=\\\"font-size:.72rem;color:var(--teal);text-decoration:none\\\">📍</a>` : '';\u001b[39m\n\u001b[31m    const byLine = r.captured_by ? `<div style=\\\"font-size:.72rem;color:var(--muted);margin-top:3px\\\">by ${esc(r.captured_by)}</div>` : '';\u001b[39m\n\u001b[31m    const clickFn = isDraft ? `openDraft(${r.id})` : `openPresentation(${r.id})`;\u001b[39m\n\u001b[31m    return `<div class=\\\"card resp-card\\\" onclick=\\\"${clickFn}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-main\\\">\u001b[39m\n\u001b[31m        <div class=\\\"name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m        <div class=\\\"company\\\">${esc(fd.company_name || '')}${(r.area||fd.area)?` · ${esc(r.area||fd.area)}`:''}</div>\u001b[39m\n\u001b[31m        <div style=\\\"display:flex;gap:6px;margin-top:6px;align-items:center;flex-wrap:wrap\\\">${draftBadge}${ltBadge}${pcBadge}${gformBadge}${locBadge}</div>\u001b[39m\n\u001b[31m        ${byLine}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"resp-card-side\\\">\u001b[39m\n\u001b[31m        <div class=\\\"date\\\">${date}</div>\u001b[39m\n\u001b[31m        ${fd.mobile ? `<div style=\\\"font-size:.75rem;color:var(--muted);margin-top:4px\\\">${esc(fd.mobile)}</div>` : ''}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  }).join('');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function openPresentation(id) {\u001b[39m\n\u001b[31m  const r = currentResponses.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  // Load photos\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    r._photos = await pr.json();\u001b[39m\n\u001b[31m  } catch(_) { r._photos = []; }\u001b[39m\n\u001b[31m  showPresentation(r);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Presentation ────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction buildPresentation(r) {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const myRole = getMyRole(a);\u001b[39m\n\u001b[31m  const isAdmin = myRole === 'admin';\u001b[39m\n\u001b[31m  const canCapture = myRole === 'admin' || myRole === 'capture' || (session.type==='client' && session.role==='capture');\u001b[39m\n\u001b[31m  const editBtn = document.getElementById('pres-edit-btn');\u001b[39m\n\u001b[31m  if (editBtn) editBtn.style.display = canCapture ? '' : 'none';\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const photos = r._photos || [];\u001b[39m\n\u001b[31m  const leadType = fd.lead_type || '';\u001b[39m\n\u001b[31m  const leadColor = leadType === 'Hot' ? 'var(--hot)' : leadType === 'Warm' ? 'var(--warm)' : leadType === 'Cold' ? 'var(--cold)' : 'var(--muted)';\u001b[39m\n\u001b[31m  const date = new Date(r.submitted_at).toLocaleString('en-IN',{day:'numeric',month:'short',year:'numeric',hour:'2-digit',minute:'2-digit'});·\u001b[39m\n\u001b[31m  let fieldsHtml = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const val = fd[f.key];\u001b[39m\n\u001b[31m    if (!val) return;\u001b[39m\n\u001b[31m    const isWide = f.type === 'textarea' || f.key === 'area' || f.key === 'remarks';\u001b[39m\n\u001b[31m    fieldsHtml += `<div class=\\\"pres-field${isWide?' wide':''}\\\">\u001b[39m\n\u001b[31m      <div class=\\\"lbl\\\">${esc(f.label)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"val\\\">${esc(val)}</div>\u001b[39m\n\u001b[31m    </div>`;\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  let photosHtml = '';\u001b[39m\n\u001b[31m  if (photos.length) {\u001b[39m\n\u001b[31m    lbPhotos = photos.map(p => p.url);\u001b[39m\n\u001b[31m    photos.forEach((p, i) => {\u001b[39m\n\u001b[31m      const isFullWidth = photos.length % 2 === 1 && i === photos.length - 1;\u001b[39m\n\u001b[31m      photosHtml += `<div class=\\\"pres-photo${isFullWidth?' full':''}\\\" onclick=\\\"openLightbox(lbPhotos,${i})\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" loading=\\\"lazy\\\" alt=\\\"Photo ${i+1}\\\">\u001b[39m\n\u001b[31m      </div>`;\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Contact action buttons\u001b[39m\n\u001b[31m  const phone = fd.mobile || fd.phone || '';\u001b[39m\n\u001b[31m  const email = fd.email || '';\u001b[39m\n\u001b[31m  const locCoords = r.location || '';\u001b[39m\n\u001b[31m  let contactActionsHtml = '';\u001b[39m\n\u001b[31m  if (phone || email || locCoords) {\u001b[39m\n\u001b[31m    const cleanPhone = phone.replace(/\\\\D/g,'');\u001b[39m\n\u001b[31m    const waPhone = cleanPhone.startsWith('91') ? cleanPhone : cleanPhone.length === 10 ? '91'+cleanPhone : cleanPhone;\u001b[39m\n\u001b[31m    let acts = '';\u001b[39m\n\u001b[31m    if (phone) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"tel:${esc(phone)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">📞</span>Call</a>`;\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://wa.me/${waPhone}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">💬</span>WhatsApp</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (locCoords) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"https://maps.google.com/?q=${encodeURIComponent(locCoords)}\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">🗺️</span>Navigate</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (email) {\u001b[39m\n\u001b[31m      acts += `<a href=\\\"mailto:${esc(email)}\\\" class=\\\"contact-btn\\\"><span class=\\\"cb-icon\\\">✉️</span>Email</a>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    contactActionsHtml = `<div class=\\\"contact-actions\\\">${acts}</div>`;\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  document.getElementById('pres-body').innerHTML = `\u001b[39m\n\u001b[31m    <div class=\\\"pres-hero\\\">\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-name\\\">${esc(fd.person_name || '(no name)')}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-company\\\">${esc(fd.company_name || a.title)}</div>\u001b[39m\n\u001b[31m      <div class=\\\"pres-hero-meta\\\">\u001b[39m\n\u001b[31m        ${leadType ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:${leadColor};\\\">${leadType} Lead</span>` : ''}\u001b[39m\n\u001b[31m        ${fd.mobile ? `<span class=\\\"badge\\\" style=\\\"background:rgba(0,0,0,.3);color:#fff\\\">📱 ${esc(fd.mobile)}</span>` : ''}\u001b[39m\n\u001b[31m        ${r.gform_submitted\u001b[39m\n\u001b[31m          ? `<span class=\\\"badge\\\" style=\\\"background:rgba(34,197,94,.2);color:var(--green)\\\">✓ GForm</span>`\u001b[39m\n\u001b[31m          : (a.gform_url ? `<span class=\\\"badge\\\" style=\\\"background:rgba(245,158,11,.2);color:var(--amber)\\\">⚠ GForm pending</span>` : '')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${contactActionsHtml}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Details</h3>\u001b[39m\n\u001b[31m      <div class=\\\"pres-fields\\\">${fieldsHtml}</div>\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${photos.length ? `<div class=\\\"pres-section\\\" style=\\\"padding-bottom:0\\\"><h3>Photos (${photos.length})</h3></div>\u001b[39m\n\u001b[31m    <div class=\\\"pres-photos\\\">${photosHtml}</div>` : (r.photo_count > 0 ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <div style=\\\"background:rgba(245,158,11,.12);border:1px solid rgba(245,158,11,.3);border-radius:10px;padding:14px 16px;color:var(--amber);font-size:.9rem;line-height:1.5\\\">\u001b[39m\n\u001b[31m        ⚠️ ${r.photo_count} photo${r.photo_count!==1?'s were':' was'} recorded but could not be uploaded (possibly due to a network issue).<br>\u001b[39m\n\u001b[31m        ${canCapture ? 'Tap 📸 above to re-add the photos.' : 'Please ask the submitter to re-add the photos.'}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : '')}\u001b[39m\n\u001b[31m    ${!r.gform_submitted && a.gform_url ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button class=\\\"btn-primary\\\" id=\\\"gform-retry-btn\\\" onclick=\\\"resubmitGForm()\\\" style=\\\"margin-top:0\\\">\u001b[39m\n\u001b[31m        📤 Submit to Google Form\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-retry-status\\\" style=\\\"font-size:.8rem;color:var(--muted);margin-top:8px;min-height:18px\\\"></div>\u001b[39m\n\u001b[31m      <button onclick=\\\"toggleGFormManual()\\\" style=\\\"background:none;border:none;color:var(--muted);font-size:.76rem;cursor:pointer;padding:4px 0;text-decoration:underline;margin-top:2px;display:block\\\">📋 Fill form manually instead</button>\u001b[39m\n\u001b[31m      <div id=\\\"gform-manual-section\\\" style=\\\"display:none;margin-top:10px;padding:12px 14px;background:rgba(99,102,241,.07);border:1px solid rgba(99,102,241,.2);border-radius:10px\\\">\u001b[39m\n\u001b[31m        <div style=\\\"font-size:.78rem;color:var(--muted);margin-bottom:10px;line-height:1.45\\\">Open the Google Form in your browser, fill it in, then tap <strong style=\\\"color:var(--text)\\\">Mark Done</strong> below to record the submission here.</div>\u001b[39m\n\u001b[31m        <button class=\\\"btn-sec\\\" onclick=\\\"openGFormManually()\\\" style=\\\"margin-top:0;margin-bottom:8px;width:100%\\\">🔗 Open Google Form</button>\u001b[39m\n\u001b[31m        <button id=\\\"gform-mark-btn\\\" onclick=\\\"markGFormSubmitted()\\\" style=\\\"width:100%;padding:11px;border-radius:var(--radius);background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:var(--green);font-weight:600;font-size:.9rem;cursor:pointer\\\">✓ Mark as GForm Submitted</button>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    ${isAdmin ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <button id=\\\"del-resp-btn\\\" onclick=\\\"confirmDelete()\\\"\u001b[39m\n\u001b[31m        style=\\\"width:100%;padding:12px;border-radius:var(--radius);font-size:.9rem;font-weight:600;\u001b[39m\n\u001b[31m               border:1px solid rgba(239,68,68,.3);background:none;color:var(--red);transition:.2s;cursor:pointer\\\">\u001b[39m\n\u001b[31m        🗑️ Delete Response\u001b[39m\n\u001b[31m      </button>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\" style=\\\"color:var(--muted);font-size:.75rem\\\">\u001b[39m\n\u001b[31m      Captured: ${date} · by ${esc(r.captured_by || 'unknown')}\u001b[39m\n\u001b[31m    </div>\u001b[39m\n\u001b[31m    ${ADMIN_IDS.includes(session.id) && r.draft_at ? `\u001b[39m\n\u001b[31m    <div class=\\\"pres-section\\\">\u001b[39m\n\u001b[31m      <h3>Audit Trail</h3>\u001b[39m\n\u001b[31m      <div class=\\\"audit-panel\\\">\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">📝 Draft saved</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.draft_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        <div class=\\\"audit-row\\\">\u001b[39m\n\u001b[31m          <span class=\\\"audit-lbl\\\">✅ Submitted</span>\u001b[39m\n\u001b[31m          <span class=\\\"audit-val\\\">${fmtISTDatetime(r.submitted_at)}</span>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    </div>` : ''}\u001b[39m\n\u001b[31m  `;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction shareResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const text = `*${fd.person_name || ''}* (${fd.company_name || ''})\\\\n📱 ${fd.mobile || ''}\\\\n🏷️ ${fd.lead_type || ''} Lead\\\\n📍 ${r.area || fd.area || ''}`;\u001b[39m\n\u001b[31m  if (navigator.share) {\u001b[39m\n\u001b[31m    navigator.share({ title: 'Response', text }).catch(()=>{});\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    navigator.clipboard?.writeText(text).then(()=>toast('Copied to clipboard','ok'));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function resubmitGForm() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a?.gform_url) return;·\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-retry-btn');\u001b[39m\n\u001b[31m  const status = document.getElementById('gform-retry-status');\u001b[39m\n\u001b[31m  btn.disabled = true;\u001b[39m\n\u001b[31m  btn.innerHTML = `<span class=\\\"spinner\\\"></span> Submitting…`;\u001b[39m\n\u001b[31m  status.textContent = '';·\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  const gFields = {};\u001b[39m\n\u001b[31m  schema.forEach(f => { if (f.gform_entry && fd[f.key]) gFields[f.gform_entry] = fd[f.key]; });·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const gr = await fetch(`${PROXY}/gform-submit`, {\u001b[39m\n\u001b[31m      method: 'POST', headers: {'Content-Type':'application/json'},\u001b[39m\n\u001b[31m      body: JSON.stringify({ url: a.gform_url, fields: gFields })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    const gd = await gr.json();\u001b[39m\n\u001b[31m    if (!gd.ok) throw new Error(gd.error || 'Submission failed');·\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true }) });\u001b[39m\n\u001b[31m    r.gform_submitted = true;·\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--green)\\\">✓ Submitted successfully</span>`;\u001b[39m\n\u001b[31m    btn.innerHTML = '✓ Submitted';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(34,197,94,.2)';\u001b[39m\n\u001b[31m    btn.style.color = 'var(--green)';\u001b[39m\n\u001b[31m    // Refresh the hero badge without rebuilding the whole page\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    status.innerHTML = `<span style=\\\"color:var(--red)\\\">Failed: ${esc(e.message)}</span>`;\u001b[39m\n\u001b[31m    btn.disabled = false;\u001b[39m\n\u001b[31m    btn.innerHTML = '📤 Retry Google Form';\u001b[39m\n\u001b[31m    const manual = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m    if (manual) manual.style.display = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction toggleGFormManual() {\u001b[39m\n\u001b[31m  const sec = document.getElementById('gform-manual-section');\u001b[39m\n\u001b[31m  if (sec) sec.style.display = sec.style.display === 'none' ? '' : 'none';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openGFormManually() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!a?.gform_url) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r?.form_data || {};·\u001b[39m\n\u001b[31m  // Resolve real form URL — handle GAS wrapper URLs (extract furl= param)\u001b[39m\n\u001b[31m  let formUrl = a.gform_url;\u001b[39m\n\u001b[31m  if (formUrl.includes('script.google.com/macros')) {\u001b[39m\n\u001b[31m    try {\u001b[39m\n\u001b[31m      const furl = new URL(formUrl).searchParams.get('furl');\u001b[39m\n\u001b[31m      if (furl) formUrl = furl;\u001b[39m\n\u001b[31m    } catch(_) {}\u001b[39m\n\u001b[31m  }·\u001b[39m\n\u001b[31m  // Strip to /viewform endpoint\u001b[39m\n\u001b[31m  const base = formUrl.split('?')[0].split('#')[0].replace(/\\\\/(viewform|formResponse)\\\\s*$/, '');\u001b[39m\n\u001b[31m  const viewUrl = base + '/viewform';·\u001b[39m\n\u001b[31m  // Build pre-filled query if gform_entry mappings exist in form schema\u001b[39m\n\u001b[31m  const params = [];\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    if (f.gform_entry && fd[f.key]) {\u001b[39m\n\u001b[31m      params.push(encodeURIComponent(f.gform_entry) + '=' + encodeURIComponent(fd[f.key]));\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });·\u001b[39m\n\u001b[31m  window.open(params.length ? viewUrl + '?' + params.join('&') : viewUrl, '_blank');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function markGFormSubmitted() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  const btn = document.getElementById('gform-mark-btn');\u001b[39m\n\u001b[31m  if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ gform_submitted: true })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    r.gform_submitted = true;\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    if (btn) { btn.disabled = false; btn.textContent = '✓ Mark as GForm Submitted'; }\u001b[39m\n\u001b[31m    toast('Failed to save.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Manage (Admin) ──────────────────────────────────── */\u001b[39m\n\u001b[31masync function buildManage() {\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const el = document.getElementById('manage-body');\u001b[39m\n\u001b[31m  el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⏳</div><p>Loading…</p></div>`;·\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const [teamR, clientR, compR] = await Promise.all([\u001b[39m\n\u001b[31m      fetch(`${API}/activity_team?activity_id=eq.${encodeURIComponent(a.id)}`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/client_access?activity_id=eq.${encodeURIComponent(a.id)}&order=created_at.desc`, { headers: ACT_RD }),\u001b[39m\n\u001b[31m      fetch(`${API}/companies?order=name.asc`, { headers: ACT_RD })\u001b[39m\n\u001b[31m    ]);\u001b[39m\n\u001b[31m    const team = await teamR.json();\u001b[39m\n\u001b[31m    const clients = await clientR.json();\u001b[39m\n\u001b[31m    clientCompanies = await compR.json();·\u001b[39m\n\u001b[31m    let html = `\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Team Members</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddTeam()\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${team.map(t => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div><div class=\\\"name\\\">${esc(t.employee_id)}</div><div class=\\\"sub\\\">${esc(t.role)}</div></div>\u001b[39m\n\u001b[31m          ${t.employee_id !== 'harish' && t.employee_id !== 'pramod' ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"removeTeamMember('${esc(t.employee_id)}')\\\">Remove</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('')}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Client Access</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openAddClient()\\\">+ Grant</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clients.length ? clients.map(c => {\u001b[39m\n\u001b[31m          const co = clientCompanies.find(x => x.id === c.company_id);\u001b[39m\n\u001b[31m          return `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m            <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m              <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m              <div class=\\\"sub\\\">Code: <strong>${esc(c.access_code)}</strong> · ${esc(c.role)}${c.phone?' · '+c.phone:''}${co?' · '+esc(co.name):''}</div>\u001b[39m\n\u001b[31m            </div>\u001b[39m\n\u001b[31m            <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Revoked'}</span>\u001b[39m\n\u001b[31m            ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"revokeClient(${c.id})\\\">Revoke</button>` : ''}\u001b[39m\n\u001b[31m          </div>`;\u001b[39m\n\u001b[31m        }).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No client access granted yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m      <div class=\\\"admin-section\\\">\u001b[39m\n\u001b[31m        <div class=\\\"admin-section-hdr\\\">\u001b[39m\n\u001b[31m          <h3>Companies</h3>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(0)\\\">+ Add</button>\u001b[39m\n\u001b[31m        </div>\u001b[39m\n\u001b[31m        ${clientCompanies.length ? clientCompanies.map(c => `<div class=\\\"admin-item\\\">\u001b[39m\n\u001b[31m          <div style=\\\"flex:1\\\">\u001b[39m\n\u001b[31m            <div class=\\\"name\\\">${esc(c.name)}</div>\u001b[39m\n\u001b[31m            <div class=\\\"sub\\\">Code: <strong>${esc(c.code)}</strong></div>\u001b[39m\n\u001b[31m          </div>\u001b[39m\n\u001b[31m          <span class=\\\"badge ${c.active?'badge-green':'badge-red'}\\\">${c.active?'Active':'Inactive'}</span>\u001b[39m\n\u001b[31m          <button class=\\\"small-btn\\\" onclick=\\\"openCompanyModal(${c.id})\\\">Edit</button>\u001b[39m\n\u001b[31m          ${c.active ? `<button class=\\\"small-btn small-btn-red\\\" onclick=\\\"deactivateCompany(${c.id})\\\">Disable</button>` : ''}\u001b[39m\n\u001b[31m        </div>`).join('') : `<div class=\\\"admin-item\\\"><div class=\\\"sub\\\">No companies yet.</div></div>`}\u001b[39m\n\u001b[31m      </div>\u001b[39m\n\u001b[31m    `;\u001b[39m\n\u001b[31m    el.innerHTML = html;\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    el.innerHTML = `<div class=\\\"empty\\\"><div class=\\\"ico\\\">⚠️</div><p>Failed to load.</p></div>`;\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddTeam() { document.getElementById('tm-emp-id').value=''; document.getElementById('add-team-modal').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeAddTeam() { document.getElementById('add-team-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveTeamMember() {\u001b[39m\n\u001b[31m  const empId = document.getElementById('tm-emp-id').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  const role = document.getElementById('tm-role').value;\u001b[39m\n\u001b[31m  if (!empId) { toast('Employee ID required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/activity_team`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, employee_id: empId, role }) });\u001b[39m\n\u001b[31m    if (!r.ok) { const e=await r.json(); throw new Error(e.message||'error'); }\u001b[39m\n\u001b[31m    closeAddTeam(); buildManage(); toast('Team member added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast(e.message || 'Error','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function removeTeamMember(empId) {\u001b[39m\n\u001b[31m  if (!confirm(`Remove ${empId} from this activity?`)) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/activity_team?activity_id=eq.${currentActivity.id}&employee_id=eq.${empId}`, { method:'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m  buildManage(); toast('Removed','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openAddClient() {\u001b[39m\n\u001b[31m  document.getElementById('ca-name').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-phone').value='';\u001b[39m\n\u001b[31m  document.getElementById('ca-code').value='';\u001b[39m\n\u001b[31m  const compSel = document.getElementById('ca-company-id');\u001b[39m\n\u001b[31m  compSel.innerHTML = `<option value=\\\"\\\">— select company —</option>` +\u001b[39m\n\u001b[31m    clientCompanies.filter(c => c.active).map(c => `<option value=\\\"${c.id}\\\">${esc(c.name)}</option>`).join('');\u001b[39m\n\u001b[31m  document.getElementById('add-client-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeAddClient() { document.getElementById('add-client-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveClientAccess() {\u001b[39m\n\u001b[31m  const name = document.getElementById('ca-name').value.trim();\u001b[39m\n\u001b[31m  const phone = document.getElementById('ca-phone').value.trim();\u001b[39m\n\u001b[31m  const role = document.getElementById('ca-role').value;\u001b[39m\n\u001b[31m  const code = (document.getElementById('ca-code').value.trim().toUpperCase() || randCode());\u001b[39m\n\u001b[31m  const companyId = document.getElementById('ca-company-id').value;\u001b[39m\n\u001b[31m  if (!name) { toast('Name required','err'); return; }\u001b[39m\n\u001b[31m  if (!companyId) { toast('Select a company','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const r = await fetch(`${API}/client_access`, { method:'POST', headers: ACT_H, body: JSON.stringify({ activity_id: currentActivity.id, name, phone: phone||null, access_code: code, role, company_id: parseInt(companyId) }) });\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeAddClient(); buildManage(); toast(`Code: ${code} — share with ${name}`,'ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function revokeClient(id) {\u001b[39m\n\u001b[31m  if (!confirm('Revoke this access code?')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/client_access?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Access revoked','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction openCompanyModal(id) {\u001b[39m\n\u001b[31m  editingCompanyId = id || 0;\u001b[39m\n\u001b[31m  document.getElementById('company-modal-title').textContent = id ? 'Edit Company' : 'Add Company';\u001b[39m\n\u001b[31m  if (id) {\u001b[39m\n\u001b[31m    const co = clientCompanies.find(c => c.id === id);\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = co ? co.name : '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = co ? co.code : '';\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    document.getElementById('cm-name').value = '';\u001b[39m\n\u001b[31m    document.getElementById('cm-code').value = '';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  document.getElementById('add-company-modal').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeCompanyModal() { document.getElementById('add-company-modal').classList.remove('open'); }·\u001b[39m\n\u001b[31masync function saveCompany() {\u001b[39m\n\u001b[31m  const name = document.getElementById('cm-name').value.trim();\u001b[39m\n\u001b[31m  const code = document.getElementById('cm-code').value.trim().toLowerCase();\u001b[39m\n\u001b[31m  if (!name) { toast('Company name required','err'); return; }\u001b[39m\n\u001b[31m  if (!code) { toast('Company code required','err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    let r;\u001b[39m\n\u001b[31m    if (editingCompanyId) {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies?id=eq.${editingCompanyId}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      r = await fetch(`${API}/companies`, { method:'POST', headers: ACT_H, body: JSON.stringify({ name, code }) });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (!r.ok) throw new Error('error');\u001b[39m\n\u001b[31m    closeCompanyModal(); buildManage(); toast(editingCompanyId ? 'Company updated' : 'Company added','ok');\u001b[39m\n\u001b[31m  } catch(e) { toast('Error saving company','err'); }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deactivateCompany(id) {\u001b[39m\n\u001b[31m  if (!confirm('Disable this company? Existing client access records will not be deleted.')) return;\u001b[39m\n\u001b[31m  await fetch(`${API}/companies?id=eq.${id}`, { method:'PATCH', headers: ACT_H, body: JSON.stringify({active:false}) });\u001b[39m\n\u001b[31m  buildManage(); toast('Company disabled','ok');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction randCode() { return Math.random().toString(36).slice(2,8).toUpperCase(); }·\u001b[39m\n\u001b[31m/* ─── Lightbox ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openLightbox(photoUrls, idx) {\u001b[39m\n\u001b[31m  lbPhotos = photoUrls; lbIdx = idx;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m  document.getElementById('lightbox').classList.add('open');\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction closeLightbox() { document.getElementById('lightbox').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction lbNav(dir) {\u001b[39m\n\u001b[31m  lbIdx = (lbIdx + dir + lbPhotos.length) % lbPhotos.length;\u001b[39m\n\u001b[31m  document.getElementById('lb-img').src = lbPhotos[lbIdx];\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Settings ────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction openSettings() { document.getElementById('settings-overlay').classList.add('open'); }\u001b[39m\n\u001b[31mfunction closeSettings() { document.getElementById('settings-overlay').classList.remove('open'); }\u001b[39m\n\u001b[31mfunction updateQuality(v) {\u001b[39m\n\u001b[31m  localStorage.setItem(QUAL_KEY, v);\u001b[39m\n\u001b[31m  document.getElementById('qual-label').textContent = `Default: ${v}%`;\u001b[39m\n\u001b[31m  const badge = document.getElementById('qual-badge');\u001b[39m\n\u001b[31m  if (badge) badge.textContent = `Quality: ${v}%`;\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31mfunction logout() {\u001b[39m\n\u001b[31m  // ADR-112: clear the shared session too — logging out here must log the user out\u001b[39m\n\u001b[31m  // of every PWA, not just Activity.\u001b[39m\n\u001b[31m  localStorage.removeItem(SESSION_KEY);\u001b[39m\n\u001b[31m  localStorage.removeItem('lm360-session');\u001b[39m\n\u001b[31m  session = null;\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m// ADR-112: if Activity believes the user is logged in, re-sync lm360-session from\u001b[39m\n\u001b[31m// Activity's own session right before navigating to Hub — guarantees Hub sees a\u001b[39m\n\u001b[31m// valid session at the moment of navigation, regardless of any clock/expiry drift\u001b[39m\n\u001b[31m// or independent-clearing that may have desynced the two copies since login.\u001b[39m\n\u001b[31mfunction goToHub() {\u001b[39m\n\u001b[31m  if (session && session.id && session.name) {\u001b[39m\n\u001b[31m    localStorage.setItem('lm360-session', JSON.stringify({\u001b[39m\n\u001b[31m      empId: session.id, name: session.name, role: session.role, loginAt: new Date().toISOString(),\u001b[39m\n\u001b[31m    }));\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  window.location.href = '/hub/';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Toast ───────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction toast(msg, type='') {\u001b[39m\n\u001b[31m  const t = document.getElementById('toast');\u001b[39m\n\u001b[31m  t.textContent = msg; t.className = type ? `show ${type}` : 'show';\u001b[39m\n\u001b[31m  clearTimeout(t._timer);\u001b[39m\n\u001b[31m  t._timer = setTimeout(() => { t.className = ''; }, 2800);\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Helpers ─────────────────────────────────────────── */\u001b[39m\n\u001b[31mfunction esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\\"/g,'&quot;'); }\u001b[39m\n\u001b[31mfunction fmtDateRange(s, e) {\u001b[39m\n\u001b[31m  if (!s) return '';\u001b[39m\n\u001b[31m  const sd = new Date(s).toLocaleDateString('en-IN',{day:'numeric',month:'short'});\u001b[39m\n\u001b[31m  const ed = e ? new Date(e).toLocaleDateString('en-IN',{day:'numeric',month:'short'}) : null;\u001b[39m\n\u001b[31m  return ed && ed !== sd ? `${sd} – ${ed}` : sd;\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Edit / Delete Response (Admin) ─────────────────── */\u001b[39m\n\u001b[31mfunction openEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  if (!r || !a) return;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = r.form_data || {};\u001b[39m\n\u001b[31m  let html = '';\u001b[39m\n\u001b[31m  schema.forEach(f => {\u001b[39m\n\u001b[31m    const label = `<label>${esc(f.label)}${f.required ? ' <span style=\\\"color:var(--red)\\\">*</span>' : ''}</label>`;\u001b[39m\n\u001b[31m    if (f.type === 'textarea') {\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<textarea id=\\\"ef_${f.key}\\\" rows=\\\"3\\\">${esc(fd[f.key] || '')}</textarea></div>`;\u001b[39m\n\u001b[31m    } else if (f.type === 'select' && f.options) {\u001b[39m\n\u001b[31m      const opts = f.options.map(o => `<option value=\\\"${esc(o)}\\\"${fd[f.key]===o?' selected':''}>${esc(o)}</option>`).join('');\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<select id=\\\"ef_${f.key}\\\"><option value=\\\"\\\">-- select --</option>${opts}</select></div>`;\u001b[39m\n\u001b[31m    } else {\u001b[39m\n\u001b[31m      const t = f.type === 'tel' ? 'tel' : f.type === 'email' ? 'email' : 'text';\u001b[39m\n\u001b[31m      html += `<div class=\\\"modal-row\\\">${label}<input type=\\\"${t}\\\" id=\\\"ef_${f.key}\\\" value=\\\"${esc(fd[f.key] || '')}\\\"></div>`;\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m  document.getElementById('edit-form-fields').innerHTML = html;\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closeEditResponse() {\u001b[39m\n\u001b[31m  document.getElementById('edit-response-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Photo Edit (image add / delete) ────────────────────── */\u001b[39m\n\u001b[31mlet editPhotosExisting = [];  // [{id, url, deleted}] — from DB\u001b[39m\n\u001b[31mlet editPhotosNew = [];       // [{blob, preview, file}] — not yet uploaded·\u001b[39m\n\u001b[31mfunction openPhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  if (!r) return;\u001b[39m\n\u001b[31m  editPhotosExisting = (r._photos || []).map(p => ({ id: p.id, url: p.url, deleted: false }));\u001b[39m\n\u001b[31m  editPhotosNew = [];\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.add('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction closePhotoEdit() {\u001b[39m\n\u001b[31m  cancelPhotoSrc();\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-modal').classList.remove('open');\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction renderPhotoEditModal() {\u001b[39m\n\u001b[31m  const kept = editPhotosExisting.filter(p => !p.deleted);\u001b[39m\n\u001b[31m  const total = kept.length + editPhotosNew.length;·\u001b[39m\n\u001b[31m  const grid = document.getElementById('photo-edit-grid');\u001b[39m\n\u001b[31m  grid.innerHTML =\u001b[39m\n\u001b[31m    kept.map(p => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:1.5px solid var(--border)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${esc(p.url)}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"markDeleteExisting(${p.id})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('') +\u001b[39m\n\u001b[31m    editPhotosNew.map((p, i) => `\u001b[39m\n\u001b[31m      <div style=\\\"position:relative;width:80px;height:80px;border-radius:10px;overflow:hidden;border:2px solid var(--teal)\\\">\u001b[39m\n\u001b[31m        <img src=\\\"${p.preview}\\\" style=\\\"width:100%;height:100%;object-fit:cover;display:block\\\">\u001b[39m\n\u001b[31m        <button onclick=\\\"removeNewEditPhoto(${i})\\\" style=\\\"position:absolute;top:2px;right:2px;background:rgba(239,68,68,.8);color:#fff;font-size:.6rem;width:18px;height:18px;border-radius:50%;border:none;cursor:pointer;line-height:18px;text-align:center\\\">✕</button>\u001b[39m\n\u001b[31m      </div>`).join('');·\u001b[39m\n\u001b[31m  document.getElementById('photo-edit-add-btn').style.display = total >= MAX_PHOTOS ? 'none' : '';\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction markDeleteExisting(id) {\u001b[39m\n\u001b[31m  const p = editPhotosExisting.find(x => x.id === id);\u001b[39m\n\u001b[31m  if (p) p.deleted = true;\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction removeNewEditPhoto(idx) {\u001b[39m\n\u001b[31m  editPhotosNew.splice(idx, 1);\u001b[39m\n\u001b[31m  renderPhotoEditModal();\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction addEditPhotoSlot() {\u001b[39m\n\u001b[31m  openPhotoSrc(({ blob, preview, file }) => {\u001b[39m\n\u001b[31m    editPhotosNew.push({ blob, preview, file });\u001b[39m\n\u001b[31m    renderPhotoEditModal();\u001b[39m\n\u001b[31m  });\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function savePhotoEdit() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const btn = document.getElementById('photo-edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    // Delete removed photos from DB (file stays on disk — acceptable)\u001b[39m\n\u001b[31m    const toDelete = editPhotosExisting.filter(p => p.deleted);\u001b[39m\n\u001b[31m    for (const p of toDelete) {\u001b[39m\n\u001b[31m      await fetch(`${API}/response_photos?id=eq.${p.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Upload new photos\u001b[39m\n\u001b[31m    for (const p of editPhotosNew) {\u001b[39m\n\u001b[31m      const fd = new FormData();\u001b[39m\n\u001b[31m      fd.append('response_id', r.id);\u001b[39m\n\u001b[31m      fd.append('activity_id', currentActivity.id);\u001b[39m\n\u001b[31m      fd.append('file', p.blob, `photo_${Date.now()}.jpg`);\u001b[39m\n\u001b[31m      const ur = await fetch(`${PROXY}/upload-activity`, { method: 'POST', body: fd });\u001b[39m\n\u001b[31m      if (ur.ok) {\u001b[39m\n\u001b[31m        const ud = await ur.json();\u001b[39m\n\u001b[31m        await fetch(`${API}/response_photos`, { method: 'POST', headers: ACT_H,\u001b[39m\n\u001b[31m          body: JSON.stringify([{ response_id: r.id, filename: `photo_${Date.now()}.jpg`, url: ud.url, size_bytes: ud.size_bytes || 0 }]) });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    // Reload photos from DB, update response photo_count\u001b[39m\n\u001b[31m    const pr = await fetch(`${API}/response_photos?response_id=eq.${r.id}&order=captured_at.asc`, { headers: ACT_RD });\u001b[39m\n\u001b[31m    const fresh = await pr.json();\u001b[39m\n\u001b[31m    await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'PATCH', headers: ACT_H,\u001b[39m\n\u001b[31m      body: JSON.stringify({ photo_count: fresh.length }) });\u001b[39m\n\u001b[31m    r._photos = fresh;\u001b[39m\n\u001b[31m    r.photo_count = fresh.length;\u001b[39m\n\u001b[31m    closePhotoEdit();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Photos saved', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Photos';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function saveEditResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  const a = currentActivity;\u001b[39m\n\u001b[31m  const schema = a.form_schema || [];\u001b[39m\n\u001b[31m  const fd = {};\u001b[39m\n\u001b[31m  for (const f of schema) {\u001b[39m\n\u001b[31m    const el = document.getElementById(`ef_${f.key}`);\u001b[39m\n\u001b[31m    if (el) fd[f.key] = el.value.trim();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m  const btn = document.getElementById('edit-save-btn');\u001b[39m\n\u001b[31m  btn.disabled = true; btn.textContent = 'Saving…';\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, {\u001b[39m\n\u001b[31m      method: 'PATCH', headers: ACT_H, body: JSON.stringify({ form_data: fd })\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    r.form_data = fd;\u001b[39m\n\u001b[31m    closeEditResponse();\u001b[39m\n\u001b[31m    buildPresentation(r);\u001b[39m\n\u001b[31m    toast('Response updated', 'ok');\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Save failed. Try again.', 'err');\u001b[39m\n\u001b[31m    btn.disabled = false; btn.textContent = 'Save Changes';\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mlet _delConfirmPending = false;\u001b[39m\n\u001b[31mlet _delDraftConfirmPending = false;·\u001b[39m\n\u001b[31mfunction confirmDeleteDraft() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-draft-btn');\u001b[39m\n\u001b[31m  if (!btn || !editingDraftId) return;\u001b[39m\n\u001b[31m  if (!_delDraftConfirmPending) {\u001b[39m\n\u001b[31m    _delDraftConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-draft-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Draft';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteDraftResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteDraftResponse() {\u001b[39m\n\u001b[31m  _delDraftConfirmPending = false;\u001b[39m\n\u001b[31m  const id = editingDraftId;\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== id);\u001b[39m\n\u001b[31m    editingDraftId = null; draftExistingPhotos = []; draftDeletedPhotoIds = [];\u001b[39m\n\u001b[31m    toast('Draft deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31mfunction confirmDelete() {\u001b[39m\n\u001b[31m  const btn = document.getElementById('del-resp-btn');\u001b[39m\n\u001b[31m  if (!btn) return;\u001b[39m\n\u001b[31m  if (!_delConfirmPending) {\u001b[39m\n\u001b[31m    _delConfirmPending = true;\u001b[39m\n\u001b[31m    btn.textContent = '⚠️ Tap again to confirm delete';\u001b[39m\n\u001b[31m    btn.style.background = 'rgba(239,68,68,.15)';\u001b[39m\n\u001b[31m    setTimeout(() => {\u001b[39m\n\u001b[31m      _delConfirmPending = false;\u001b[39m\n\u001b[31m      if (document.getElementById('del-resp-btn')) {\u001b[39m\n\u001b[31m        btn.textContent = '🗑️ Delete Response';\u001b[39m\n\u001b[31m        btn.style.background = 'none';\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    }, 3000);\u001b[39m\n\u001b[31m  } else {\u001b[39m\n\u001b[31m    deleteResponse();\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31masync function deleteResponse() {\u001b[39m\n\u001b[31m  const r = currentResponse;\u001b[39m\n\u001b[31m  _delConfirmPending = false;\u001b[39m\n\u001b[31m  if (!ADMIN_IDS.includes(session?.id)) { toast('Only admins can delete responses.', 'err'); return; }\u001b[39m\n\u001b[31m  try {\u001b[39m\n\u001b[31m    const res = await fetch(`${API}/responses?id=eq.${r.id}`, { method: 'DELETE', headers: ACT_H });\u001b[39m\n\u001b[31m    if (!res.ok) throw new Error();\u001b[39m\n\u001b[31m    currentResponses = currentResponses.filter(x => x.id !== r.id);\u001b[39m\n\u001b[31m    toast('Response deleted', 'ok');\u001b[39m\n\u001b[31m    showResponses();\u001b[39m\n\u001b[31m  } catch(e) {\u001b[39m\n\u001b[31m    toast('Delete failed. Try again.', 'err');\u001b[39m\n\u001b[31m  }\u001b[39m\n\u001b[31m}·\u001b[39m\n\u001b[31m/* ─── Service Worker ──────────────────────────────────── */\u001b[39m\n\u001b[31mif ('serviceWorker' in navigator) {\u001b[39m\n\u001b[31m  navigator.serviceWorker.register('/activity/sw.js').then(reg => {\u001b[39m\n\u001b[31m    function showUpdateBanner() {\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.add('show');\u001b[39m\n\u001b[31m    }\u001b[39m\n\u001b[31m    if (reg.waiting) showUpdateBanner();\u001b[39m\n\u001b[31m    reg.addEventListener('updatefound', () => {\u001b[39m\n\u001b[31m      const nw = reg.installing;\u001b[39m\n\u001b[31m      nw.addEventListener('statechange', () => {\u001b[39m\n\u001b[31m        if (nw.state === 'installed' && navigator.serviceWorker.controller) showUpdateBanner();\u001b[39m\n\u001b[31m      });\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update').addEventListener('click', () => {\u001b[39m\n\u001b[31m      if (reg.waiting) {\u001b[39m\n\u001b[31m        reg.waiting.postMessage({ type: 'SKIP_WAITING' });\u001b[39m\n\u001b[31m        navigator.serviceWorker.addEventListener('controllerchange', () => window.location.reload(), { once: true });\u001b[39m\n\u001b[31m      }\u001b[39m\n\u001b[31m    });\u001b[39m\n\u001b[31m    document.getElementById('btn-update-dismiss').addEventListener('click', () =>\u001b[39m\n\u001b[31m      document.getElementById('update-banner').classList.remove('show'));\u001b[39m\n\u001b[31m  }).catch(() => {});\u001b[39m\n\u001b[31m}\u001b[39m\n\u001b[31m</script>\u001b[39m\n\u001b[31m</body>\u001b[39m\n\u001b[31m</html>\u001b[39m\n\u001b[31m\"\u001b[39m\n\n  242 |     const html = await resp.text();\n  243 |     await ctx.dispose();\n> 244 |     expect(html).toContain(\"window.location.href='/hub/'\");\n      |                  ^\n  245 |     expect(html).toContain('🏡');\n  246 |   });\n  247 |\n    at /var/www/360lm/tests/activity.spec.js:244:18"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:42:11.291Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/activity-Activity-—-Hub-Na-c60c4--home-topbar-has-Hub-button-desktop-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/activity.spec.js",
                        "column": 18,
                        "line": 244
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "df521ef6570605d13829-455a5260a3cc4c799541",
              "file": "activity.spec.js",
              "line": 239,
              "column": 3
            },
            {
              "title": "sw.js is v25 or later",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 56,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:12.280Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "df521ef6570605d13829-3333643baa7542543de2",
              "file": "activity.spec.js",
              "line": 248,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "dispatch.spec.js",
      "file": "dispatch.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Dispatch — Smoke",
          "file": "dispatch.spec.js",
          "line": 9,
          "column": 6,
          "specs": [
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1490,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:30.620Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-63b703f798c6b89c1844",
              "file": "dispatch.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 236,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.285Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-0abf5d31404d250a90af",
              "file": "dispatch.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 230,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.548Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-37601325b18bf433a5b0",
              "file": "dispatch.spec.js",
              "line": 22,
              "column": 3
            },
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1435,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:12.380Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-79724089e7cb2fee990f",
              "file": "dispatch.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 234,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:13.949Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-8c4b10c8bbd0540d96ee",
              "file": "dispatch.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 205,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:14.212Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-ada0bd95f6a8d1b606c5",
              "file": "dispatch.spec.js",
              "line": 22,
              "column": 3
            }
          ]
        },
        {
          "title": "Dispatch — HTML Structure",
          "file": "dispatch.spec.js",
          "line": 29,
          "column": 6,
          "specs": [
            {
              "title": "all panes present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 7,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.795Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-c8dadba4d4aafc5b2f57",
              "file": "dispatch.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "hub navigation button present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.867Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-e2726a9cf4782c036a25",
              "file": "dispatch.spec.js",
              "line": 47,
              "column": 3
            },
            {
              "title": "hub auth gate present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 6,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.884Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-5405793d42ef9b57804f",
              "file": "dispatch.spec.js",
              "line": 52,
              "column": 3
            },
            {
              "title": "all panes present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 5,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:14.447Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-713d9f2b949293fae3a1",
              "file": "dispatch.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "hub navigation button present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:14.516Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-eaf7c31f791ad5c872d7",
              "file": "dispatch.spec.js",
              "line": 47,
              "column": 3
            },
            {
              "title": "hub auth gate present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:14.537Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "3510226c3ce1e611dacb-779d266fdd2a744a1b06",
              "file": "dispatch.spec.js",
              "line": 52,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "expense.spec.js",
      "file": "expense.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Expenses — Smoke",
          "file": "expense.spec.js",
          "line": 9,
          "column": 6,
          "specs": [
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1300,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:32.908Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-3bf1fb454096f73d81f8",
              "file": "expense.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 187,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.225Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-d5316b6e995b7790fe42",
              "file": "expense.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 212,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.432Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-22d3fda65423125f291f",
              "file": "expense.spec.js",
              "line": 22,
              "column": 3
            },
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1243,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:14.554Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-7d1f2eea2388ccca6d02",
              "file": "expense.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 197,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:15.810Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-1da934617a4d1cda8788",
              "file": "expense.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 280,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.024Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-d7fecb204bf6e208d449",
              "file": "expense.spec.js",
              "line": 22,
              "column": 3
            }
          ]
        },
        {
          "title": "Expenses — HTML Structure",
          "file": "expense.spec.js",
          "line": 29,
          "column": 6,
          "specs": [
            {
              "title": "all screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 8,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.679Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-cf961a4a22ce7b4adf00",
              "file": "expense.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "key functions present in source",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 6,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.731Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-1e43735bf45a18708dc4",
              "file": "expense.spec.js",
              "line": 50,
              "column": 3
            },
            {
              "title": "hub navigation is present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.752Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-fa4747e31770727c09ff",
              "file": "expense.spec.js",
              "line": 59,
              "column": 3
            },
            {
              "title": "push notification support present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.773Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-6626a912c1ac516dab54",
              "file": "expense.spec.js",
              "line": 64,
              "column": 3
            },
            {
              "title": "custodian advances integration present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 2,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.789Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-ea60ed3da9da31148844",
              "file": "expense.spec.js",
              "line": 69,
              "column": 3
            },
            {
              "title": "impress screen present for Impress Reports",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 2,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.804Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-4f6fe6df41f7cbbc8293",
              "file": "expense.spec.js",
              "line": 74,
              "column": 3
            },
            {
              "title": "all screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 7,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.330Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-543108dd5b722e4b30e7",
              "file": "expense.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "key functions present in source",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 6,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.375Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-4af52f23743a9ad7d894",
              "file": "expense.spec.js",
              "line": 50,
              "column": 3
            },
            {
              "title": "hub navigation is present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.397Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-7e3373e789c02c52b233",
              "file": "expense.spec.js",
              "line": 59,
              "column": 3
            },
            {
              "title": "push notification support present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.410Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-efdd0c31fb75f3eb1693",
              "file": "expense.spec.js",
              "line": 64,
              "column": 3
            },
            {
              "title": "custodian advances integration present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 2,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.428Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-14bcda3901c84ce603fd",
              "file": "expense.spec.js",
              "line": 69,
              "column": 3
            },
            {
              "title": "impress screen present for Impress Reports",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 2,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.440Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "2399d47327fc24358745-e4d36895cc6108ba501b",
              "file": "expense.spec.js",
              "line": 74,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "hr.spec.js",
      "file": "hr.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "HR Management — Access Control",
          "file": "hr.spec.js",
          "line": 17,
          "column": 6,
          "specs": [
            {
              "title": "no session → redirects to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 438,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:34.828Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-93f8841e1f49eb7621a4",
              "file": "hr.spec.js",
              "line": 18,
              "column": 3
            },
            {
              "title": "with valid session → home screen loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 360,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:35.278Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-a154d0175ca310e5fb89",
              "file": "hr.spec.js",
              "line": 23,
              "column": 3
            },
            {
              "title": "no session → redirects to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 438,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.459Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-98f1919840e230f3f0a8",
              "file": "hr.spec.js",
              "line": 18,
              "column": 3
            },
            {
              "title": "with valid session → home screen loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 355,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:16.911Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-d1caa549fb073e2fb62d",
              "file": "hr.spec.js",
              "line": 23,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — User Bar: Topbar Screens",
          "file": "hr.spec.js",
          "line": 30,
          "column": 6,
          "specs": [
            {
              "title": "home topbar shows user name",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 356,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:35.659Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-4dd8bc3fa551f6f2fc0b",
              "file": "hr.spec.js",
              "line": 35,
              "column": 3
            },
            {
              "title": "home topbar has Logout button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 377,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:36.047Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-0f0747cbdccb5743bbf5",
              "file": "hr.spec.js",
              "line": 40,
              "column": 3
            },
            {
              "title": "logout confirm=dismiss — stays on page",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 457,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:36.443Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-0efbece8363a26d7b996",
              "file": "hr.spec.js",
              "line": 45,
              "column": 3
            },
            {
              "title": "logout confirm=OK → redirects to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 640,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:36.916Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-93ab81e3991c7861b699",
              "file": "hr.spec.js",
              "line": 53,
              "column": 3
            },
            {
              "title": "home topbar shows user name",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 324,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:17.287Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-7d3c1892ec14e8a7f204",
              "file": "hr.spec.js",
              "line": 35,
              "column": 3
            },
            {
              "title": "home topbar has Logout button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 305,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:17.635Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-6cf2e2d98d186fc640e2",
              "file": "hr.spec.js",
              "line": 40,
              "column": 3
            },
            {
              "title": "logout confirm=dismiss — stays on page",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 426,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:17.963Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-6723e85d05fa5b06bc6e",
              "file": "hr.spec.js",
              "line": 45,
              "column": 3
            },
            {
              "title": "logout confirm=OK → redirects to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 684,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:18.404Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-6bcb167a520971d057aa",
              "file": "hr.spec.js",
              "line": 53,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — User Bar: No-Topbar Screens (injected)",
          "file": "hr.spec.js",
          "line": 62,
          "column": 6,
          "specs": [
            {
              "title": "scr-attendance gets a topbar prepended with Hub + Logout",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 475,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:37.580Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-7eec289a0151f0a85b50",
              "file": "hr.spec.js",
              "line": 67,
              "column": 3
            },
            {
              "title": "scr-attendance topbar Hub button navigates to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 782,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:38.073Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-1c46bc3e940f14e12a43",
              "file": "hr.spec.js",
              "line": 78,
              "column": 3
            },
            {
              "title": "scr-attendance gets a topbar prepended with Hub + Logout",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 573,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:19.117Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-f4a105b5880391714503",
              "file": "hr.spec.js",
              "line": 67,
              "column": 3
            },
            {
              "title": "scr-attendance topbar Hub button navigates to /hub/",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 830,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:19.710Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-ed0ffc73d9c06bd3b6a1",
              "file": "hr.spec.js",
              "line": 78,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — User Bar: Employee Role",
          "file": "hr.spec.js",
          "line": 88,
          "column": 6,
          "specs": [
            {
              "title": "employee home topbar shows their name",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 339,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:38.879Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-df62e39b02edf42734c3",
              "file": "hr.spec.js",
              "line": 93,
              "column": 3
            },
            {
              "title": "employee home topbar has Logout button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 363,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:39.237Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-4882593ca53c335695b3",
              "file": "hr.spec.js",
              "line": 98,
              "column": 3
            },
            {
              "title": "employee home topbar shows their name",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 334,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:20.569Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-acfff3368cdaf76cede0",
              "file": "hr.spec.js",
              "line": 93,
              "column": 3
            },
            {
              "title": "employee home topbar has Logout button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 395,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:20.930Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-9072c0c4372ea082848b",
              "file": "hr.spec.js",
              "line": 98,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — Existing Functionality Unchanged",
          "file": "hr.spec.js",
          "line": 104,
          "column": 6,
          "specs": [
            {
              "title": "bottom nav renders correct items for admin",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 348,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:39.632Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-2ce28d43100e8a510f28",
              "file": "hr.spec.js",
              "line": 109,
              "column": 3
            },
            {
              "title": "home content area is populated",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 356,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:40.005Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-876327021cf36189da99",
              "file": "hr.spec.js",
              "line": 116,
              "column": 3
            },
            {
              "title": "existing Hub button still works on home topbar",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 383,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:40.381Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-0ad0523966278d674a7c",
              "file": "hr.spec.js",
              "line": 122,
              "column": 3
            },
            {
              "title": "bottom nav renders correct items for admin",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 453,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:21.341Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-5b5a17f7b4f7cbc4c543",
              "file": "hr.spec.js",
              "line": 109,
              "column": 3
            },
            {
              "title": "home content area is populated",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 391,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:21.817Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-ea2223456924354025c1",
              "file": "hr.spec.js",
              "line": 116,
              "column": 3
            },
            {
              "title": "existing Hub button still works on home topbar",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 399,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:22.227Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-312c714c805cfe58aacf",
              "file": "hr.spec.js",
              "line": 122,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — Salary Advance Recovery",
          "file": "hr.spec.js",
          "line": 137,
          "column": 6,
          "specs": [
            {
              "title": "get_employee_advances_for_salary RPC is reachable (hr profile)",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 2,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 26,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:142:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 142
                        },
                        "snippet": "  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 142
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |\n    at /var/www/360lm/tests/hr.spec.js:142:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:40.797Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--c1708-PC-is-reachable-hr-profile--android-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 142
                      }
                    },
                    {
                      "workerIndex": 3,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 68,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:142:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 142
                        },
                        "snippet": "  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 142
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |\n    at /var/www/360lm/tests/hr.spec.js:142:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:40:41.740Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--c1708-PC-is-reachable-hr-profile--android-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 142
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-0e9178349aba126ae32d",
              "file": "hr.spec.js",
              "line": 138,
              "column": 3
            },
            {
              "title": "pay_salary_slip guards a non-existent slip (mutates nothing)",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 4,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 72,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:152:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 152
                        },
                        "snippet": "  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 152
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);\n    at /var/www/360lm/tests/hr.spec.js:152:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:42.728Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--68164-stent-slip-mutates-nothing--android-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 152
                      }
                    },
                    {
                      "workerIndex": 5,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 67,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:152:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 152
                        },
                        "snippet": "  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 152
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);\n    at /var/www/360lm/tests/hr.spec.js:152:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:40:43.696Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--68164-stent-slip-mutates-nothing--android-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 152
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-b3b803d3fed16d1428bd",
              "file": "hr.spec.js",
              "line": 146,
              "column": 3
            },
            {
              "title": "pay modal markup includes advance-recovery section",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 6,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 505,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:44.613Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-a012dbe1416c4e1b261a",
              "file": "hr.spec.js",
              "line": 158,
              "column": 3
            },
            {
              "title": "service worker version is current",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 6,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 206,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:45.311Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-77c829ea4e6f07a517ae",
              "file": "hr.spec.js",
              "line": 168,
              "column": 3
            },
            {
              "title": "get_employee_advances_for_salary RPC is reachable (hr profile)",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 11,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 11,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:142:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 142
                        },
                        "snippet": "  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 142
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |\n    at /var/www/360lm/tests/hr.spec.js:142:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:22.649Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--c1708-PC-is-reachable-hr-profile--desktop-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 142
                      }
                    },
                    {
                      "workerIndex": 12,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 61,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:142:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 142
                        },
                        "snippet": "  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 142
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  140 |       headers: HR_HEADERS, data: { p_employee_id: 'mukesh' }\n  141 |     });\n> 142 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  143 |     expect(Array.isArray(await r.json())).toBe(true);\n  144 |   });\n  145 |\n    at /var/www/360lm/tests/hr.spec.js:142:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:42:23.532Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--c1708-PC-is-reachable-hr-profile--desktop-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 142
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-c70fe4e5d35a47606af2",
              "file": "hr.spec.js",
              "line": 138,
              "column": 3
            },
            {
              "title": "pay_salary_slip guards a non-existent slip (mutates nothing)",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 13,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 68,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:152:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 152
                        },
                        "snippet": "  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 152
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);\n    at /var/www/360lm/tests/hr.spec.js:152:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:24.455Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--68164-stent-slip-mutates-nothing--desktop-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 152
                      }
                    },
                    {
                      "workerIndex": 14,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 74,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:152:20",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 20,
                          "line": 152
                        },
                        "snippet": "  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 20,
                            "line": 152
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n  150 |               p_reference:null, p_paid_by:'pramod', p_recoveries:[] }\n  151 |     });\n> 152 |     expect(r.ok()).toBeTruthy();\n      |                    ^\n  153 |     const d = await r.json();\n  154 |     expect(d.ok).toBe(false);\n  155 |     expect(d.error).toMatch(/not found/i);\n    at /var/www/360lm/tests/hr.spec.js:152:20"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:42:25.413Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Salary--68164-stent-slip-mutates-nothing--desktop-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 20,
                        "line": 152
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-028fbd1b5548f7845057",
              "file": "hr.spec.js",
              "line": 146,
              "column": 3
            },
            {
              "title": "pay modal markup includes advance-recovery section",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 15,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 473,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:26.423Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-8039da32a6f33158e356",
              "file": "hr.spec.js",
              "line": 158,
              "column": 3
            },
            {
              "title": "service worker version is current",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 15,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 185,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:27.073Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-1812ca435c76bc65f915",
              "file": "hr.spec.js",
              "line": 168,
              "column": 3
            }
          ]
        },
        {
          "title": "HR Management — Employee read path (web_anon)",
          "file": "hr.spec.js",
          "line": 178,
          "column": 6,
          "specs": [
            {
              "title": "GET /db/employees (expense profile) returns rows for web_anon [BUG-1]",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 6,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 79,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:45.551Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-7621dadd18dd4b6b11e0",
              "file": "hr.spec.js",
              "line": 179,
              "column": 3
            },
            {
              "title": "generate_salary_slip runs as web_anon without permission error [BUG-3]",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 6,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 23,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:202:17",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 17,
                          "line": 202
                        },
                        "snippet": "  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 17,
                            "line": 202
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n\n  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |\n    at /var/www/360lm/tests/hr.spec.js:202:17"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:45.647Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Employe-8fb1e-out-permission-error-BUG-3--android-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 17,
                        "line": 202
                      }
                    },
                    {
                      "workerIndex": 7,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 81,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:202:17",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 17,
                          "line": 202
                        },
                        "snippet": "  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 17,
                            "line": 202
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n\n  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |\n    at /var/www/360lm/tests/hr.spec.js:202:17"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:40:46.611Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Employe-8fb1e-out-permission-error-BUG-3--android-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 17,
                        "line": 202
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-090e0d9032f49fc96ef7",
              "file": "hr.spec.js",
              "line": 189,
              "column": 3
            },
            {
              "title": "GET /db/employees (expense profile) returns rows for web_anon [BUG-1]",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 15,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 73,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:27.288Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "98264c29bbadd0584463-005ecd07d98551964620",
              "file": "hr.spec.js",
              "line": 179,
              "column": 3
            },
            {
              "title": "generate_salary_slip runs as web_anon without permission error [BUG-3]",
              "ok": false,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 15,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 26,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:202:17",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 17,
                          "line": 202
                        },
                        "snippet": "  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 17,
                            "line": 202
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n\n  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |\n    at /var/www/360lm/tests/hr.spec.js:202:17"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:27.387Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Employe-8fb1e-out-permission-error-BUG-3--desktop-chrome/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 17,
                        "line": 202
                      }
                    },
                    {
                      "workerIndex": 16,
                      "parallelIndex": 0,
                      "status": "failed",
                      "duration": 73,
                      "error": {
                        "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m",
                        "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n    at /var/www/360lm/tests/hr.spec.js:202:17",
                        "location": {
                          "file": "/var/www/360lm/tests/hr.spec.js",
                          "column": 17,
                          "line": 202
                        },
                        "snippet": "  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |"
                      },
                      "errors": [
                        {
                          "location": {
                            "file": "/var/www/360lm/tests/hr.spec.js",
                            "column": 17,
                            "line": 202
                          },
                          "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoMatch\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m)\u001b[22m\n\nExpected pattern: \u001b[32m/salary structure|slip_id/i\u001b[39m\nReceived string:  \u001b[31m\"{\\\"code\\\":\\\"PGRST106\\\",\\\"details\\\":null,\\\"hint\\\":null,\\\"message\\\":\\\"The schema must be one of the following: hub, expense, sales, custodian, activity, recce, counters\\\"}\"\u001b[39m\n\n  200 |     expect(msg).not.toMatch(/42501/);\n  201 |     // It should reach business logic (no structure) — proving the employee read worked\n> 202 |     expect(msg).toMatch(/salary structure|slip_id/i);\n      |                 ^\n  203 |   });\n  204 | });\n  205 |\n    at /var/www/360lm/tests/hr.spec.js:202:17"
                        }
                      ],
                      "stdout": [],
                      "stderr": [],
                      "retry": 1,
                      "startTime": "2026-07-12T13:42:28.300Z",
                      "annotations": [],
                      "attachments": [
                        {
                          "name": "error-context",
                          "contentType": "text/markdown",
                          "path": "/var/www/360lm/test-results/hr-HR-Management-—-Employe-8fb1e-out-permission-error-BUG-3--desktop-chrome-retry1/error-context.md"
                        }
                      ],
                      "errorLocation": {
                        "file": "/var/www/360lm/tests/hr.spec.js",
                        "column": 17,
                        "line": 202
                      }
                    }
                  ],
                  "status": "unexpected"
                }
              ],
              "id": "98264c29bbadd0584463-e612f8208002c2c0a394",
              "file": "hr.spec.js",
              "line": 189,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "printing.spec.js",
      "file": "printing.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Print Nest — Page Load",
          "file": "printing.spec.js",
          "line": 20,
          "column": 6,
          "specs": [
            {
              "title": "no session → redirects to hub",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 546,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:47.617Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e9a03ed15400c0adb24e",
              "file": "printing.spec.js",
              "line": 21,
              "column": 3
            },
            {
              "title": "with session → dashboard visible",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1054,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:48.345Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-7006444a9ba7177df2b0",
              "file": "printing.spec.js",
              "line": 26,
              "column": 3
            },
            {
              "title": "manifest.json reachable",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 208,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:49.432Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-c5d807eb50919c436141",
              "file": "printing.spec.js",
              "line": 32,
              "column": 3
            },
            {
              "title": "service worker registers",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1231,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:49.676Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-1b08180ed543df04f851",
              "file": "printing.spec.js",
              "line": 40,
              "column": 3
            },
            {
              "title": "no session → redirects to hub",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 609,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:29.271Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-599665ed77b1c5a0d64d",
              "file": "printing.spec.js",
              "line": 21,
              "column": 3
            },
            {
              "title": "with session → dashboard visible",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1078,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:30.063Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-517b817b11b2ee7ec7ee",
              "file": "printing.spec.js",
              "line": 26,
              "column": 3
            },
            {
              "title": "manifest.json reachable",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 173,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:31.176Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-31d2c9edd77ee2f30204",
              "file": "printing.spec.js",
              "line": 32,
              "column": 3
            },
            {
              "title": "service worker registers",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1107,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:31.365Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-b63a0ee0f09ecbe80cab",
              "file": "printing.spec.js",
              "line": 40,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Dashboard",
          "file": "printing.spec.js",
          "line": 52,
          "column": 6,
          "specs": [
            {
              "title": "stat boxes visible",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1151,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:50.935Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-fa405ea4ee770d0637ea",
              "file": "printing.spec.js",
              "line": 55,
              "column": 3
            },
            {
              "title": "user name shown in header",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1075,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:52.126Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e9f3cdfe74b455808006",
              "file": "printing.spec.js",
              "line": 63,
              "column": 3
            },
            {
              "title": "Start New Nesting button navigates to Add screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1169,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:53.218Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-47f5bb5b71ebdce00a51",
              "file": "printing.spec.js",
              "line": 69,
              "column": 3
            },
            {
              "title": "bottom nav Runs tab loads runs screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1127,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:54.410Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-7a666b0a97a452261553",
              "file": "printing.spec.js",
              "line": 76,
              "column": 3
            },
            {
              "title": "bottom nav Stock tab loads stock screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1139,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:55.553Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-cc32a17fcdcd303b5530",
              "file": "printing.spec.js",
              "line": 83,
              "column": 3
            },
            {
              "title": "stat boxes visible",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1067,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:32.496Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e1dba80098461a38f898",
              "file": "printing.spec.js",
              "line": 55,
              "column": 3
            },
            {
              "title": "user name shown in header",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1078,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:33.585Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-9ca22474619662f7c0bc",
              "file": "printing.spec.js",
              "line": 63,
              "column": 3
            },
            {
              "title": "Start New Nesting button navigates to Add screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1117,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:34.694Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-44fb3cd490b0d388c929",
              "file": "printing.spec.js",
              "line": 69,
              "column": 3
            },
            {
              "title": "bottom nav Runs tab loads runs screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1100,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:35.828Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-d4ac55b6502fc643652d",
              "file": "printing.spec.js",
              "line": 76,
              "column": 3
            },
            {
              "title": "bottom nav Stock tab loads stock screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1176,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:36.949Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-1a95200c08fd862f955b",
              "file": "printing.spec.js",
              "line": 83,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Add Items (Manual)",
          "file": "printing.spec.js",
          "line": 91,
          "column": 6,
          "specs": [
            {
              "title": "Compute button disabled with no items",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1208,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:56.716Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e179d9dfe158529b0fb2",
              "file": "printing.spec.js",
              "line": 102,
              "column": 3
            },
            {
              "title": "empty form shows validation toast",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1274,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:57.943Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-d4910943ff27bc3b36a2",
              "file": "printing.spec.js",
              "line": 106,
              "column": 3
            },
            {
              "title": "valid item adds to list and enables Compute",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1595,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:40:59.233Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-54e05ee6fecacfc0cee7",
              "file": "printing.spec.js",
              "line": 111,
              "column": 3
            },
            {
              "title": "removing item disables Compute again",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1650,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:00.843Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-399d972b0fcd44fa6fe8",
              "file": "printing.spec.js",
              "line": 128,
              "column": 3
            },
            {
              "title": "multiple items accumulate in list",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1821,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:02.515Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-622332b7a31b39260603",
              "file": "printing.spec.js",
              "line": 139,
              "column": 3
            },
            {
              "title": "bleed margins section expands",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1295,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:04.368Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-b89251c9b0cdd647b45e",
              "file": "printing.spec.js",
              "line": 151,
              "column": 3
            },
            {
              "title": "Compute button disabled with no items",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1187,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:38.145Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-c2394bb0a2591953ace2",
              "file": "printing.spec.js",
              "line": 102,
              "column": 3
            },
            {
              "title": "empty form shows validation toast",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1201,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:39.353Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-a2c2aed7551e2edd1e70",
              "file": "printing.spec.js",
              "line": 106,
              "column": 3
            },
            {
              "title": "valid item adds to list and enables Compute",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1275,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:40.568Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-c4a183df5d2e20dec15e",
              "file": "printing.spec.js",
              "line": 111,
              "column": 3
            },
            {
              "title": "removing item disables Compute again",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1399,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:41.860Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-4550ef4778ee5e898c37",
              "file": "printing.spec.js",
              "line": 128,
              "column": 3
            },
            {
              "title": "multiple items accumulate in list",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1537,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:43.275Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-72cae6b844b178f8b174",
              "file": "printing.spec.js",
              "line": 139,
              "column": 3
            },
            {
              "title": "bleed margins section expands",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1338,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:44.829Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-2e472695fc5bf56ec57f",
              "file": "printing.spec.js",
              "line": 151,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Nesting Engine",
          "file": "printing.spec.js",
          "line": 158,
          "column": 6,
          "specs": [
            {
              "title": "3×6 Flex → 1-roll 3ft, 18 sqft/pc (narrow=3 → 3ft roll)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1420,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:05.690Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-5cafecf53aee7e1b2b16",
              "file": "printing.spec.js",
              "line": 178,
              "column": 3
            },
            {
              "title": "7ft wide Flex → 2-roll (3+4ft) is available",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1581,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:07.131Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-70ff6019d0a5c87828da",
              "file": "printing.spec.js",
              "line": 193,
              "column": 3
            },
            {
              "title": "item >5ft shows outsource/join buttons",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1432,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:08.728Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-7e40f609da5de5265042",
              "file": "printing.spec.js",
              "line": 207,
              "column": 3
            },
            {
              "title": "gang plan groups same roll-width items together",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1618,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:10.176Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-6e27e03e92e572b90617",
              "file": "printing.spec.js",
              "line": 220,
              "column": 3
            },
            {
              "title": "Flex and Vinyl items split into separate groups",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1634,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:11.812Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-73997ee3ee4043d443ea",
              "file": "printing.spec.js",
              "line": 234,
              "column": 3
            },
            {
              "title": "Save button opens save modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1521,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:13.473Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-1c6758088a97dceed358",
              "file": "printing.spec.js",
              "line": 242,
              "column": 3
            },
            {
              "title": "AI review button visible after computing nest",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1405,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:15.015Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-9e107b68e8911f92ed23",
              "file": "printing.spec.js",
              "line": 250,
              "column": 3
            },
            {
              "title": "AI review button opens chat modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1484,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:16.449Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-189c1833ae2688412e71",
              "file": "printing.spec.js",
              "line": 258,
              "column": 3
            },
            {
              "title": "Copy plan button visible on nest screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1419,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:17.954Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-393eaf8c973ebe982a39",
              "file": "printing.spec.js",
              "line": 268,
              "column": 3
            },
            {
              "title": "Copy plan writes text to clipboard and shows toast",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1517,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:19.392Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-09429b972abab1567049",
              "file": "printing.spec.js",
              "line": 275,
              "column": 3
            },
            {
              "title": "3×6 Flex → 1-roll 3ft, 18 sqft/pc (narrow=3 → 3ft roll)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1420,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:46.190Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-47c53cb59920ccacabce",
              "file": "printing.spec.js",
              "line": 178,
              "column": 3
            },
            {
              "title": "7ft wide Flex → 2-roll (3+4ft) is available",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1524,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:47.630Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-5db5e7122f08c65c6e5d",
              "file": "printing.spec.js",
              "line": 193,
              "column": 3
            },
            {
              "title": "item >5ft shows outsource/join buttons",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1467,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:49.175Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-8c7c13efddd6faf596bd",
              "file": "printing.spec.js",
              "line": 207,
              "column": 3
            },
            {
              "title": "gang plan groups same roll-width items together",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1625,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:50.662Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-484a882ae5569bbd49d4",
              "file": "printing.spec.js",
              "line": 220,
              "column": 3
            },
            {
              "title": "Flex and Vinyl items split into separate groups",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1576,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:52.316Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-4419a71daf3798747a1f",
              "file": "printing.spec.js",
              "line": 234,
              "column": 3
            },
            {
              "title": "Save button opens save modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1488,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:53.910Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-46cfaee8b74a0d61cc70",
              "file": "printing.spec.js",
              "line": 242,
              "column": 3
            },
            {
              "title": "AI review button visible after computing nest",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1382,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:55.420Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-2b6272c63008b807964a",
              "file": "printing.spec.js",
              "line": 250,
              "column": 3
            },
            {
              "title": "AI review button opens chat modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1489,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:56.821Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-0f5371b95d2c82a0779f",
              "file": "printing.spec.js",
              "line": 258,
              "column": 3
            },
            {
              "title": "Copy plan button visible on nest screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1369,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:58.334Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-4d08a127616c207c0cef",
              "file": "printing.spec.js",
              "line": 268,
              "column": 3
            },
            {
              "title": "Copy plan writes text to clipboard and shows toast",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1624,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:59.719Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-0adb747481eb55b6290d",
              "file": "printing.spec.js",
              "line": 275,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Excel/AI Tab",
          "file": "printing.spec.js",
          "line": 291,
          "column": 6,
          "specs": [
            {
              "title": "Excel tab is active by default on add screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1216,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:20.933Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-472a3a2ce3bb0bf75b89",
              "file": "printing.spec.js",
              "line": 300,
              "column": 3
            },
            {
              "title": "Excel/AI tab shows upload area",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1122,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:22.171Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-a1bf996ecafcd8c382c6",
              "file": "printing.spec.js",
              "line": 307,
              "column": 3
            },
            {
              "title": "Layout button present on nest screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1295,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:23.311Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-f533009b19f0a1a2749a",
              "file": "printing.spec.js",
              "line": 312,
              "column": 3
            },
            {
              "title": "Compute button pulses when items exist",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1257,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:24.628Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-164ab48d656211accbec",
              "file": "printing.spec.js",
              "line": 325,
              "column": 3
            },
            {
              "title": "Excel tab is active by default on add screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1396,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:01.362Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-5d797116170cf6ab836e",
              "file": "printing.spec.js",
              "line": 300,
              "column": 3
            },
            {
              "title": "Excel/AI tab shows upload area",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1394,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:02.788Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-5f8c798e8c9d22dd5e2e",
              "file": "printing.spec.js",
              "line": 307,
              "column": 3
            },
            {
              "title": "Layout button present on nest screen",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1500,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:04.204Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-179fbaafd55252cf489b",
              "file": "printing.spec.js",
              "line": 312,
              "column": 3
            },
            {
              "title": "Compute button pulses when items exist",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1379,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:05.726Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-6aff064d370b9278838f",
              "file": "printing.spec.js",
              "line": 325,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — 2D Lane Nesting",
          "file": "printing.spec.js",
          "line": 337,
          "column": 6,
          "specs": [
            {
              "title": "demo button loads 4 items",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1161,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:25.905Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-30dfdd4a3bca0080ff75",
              "file": "printing.spec.js",
              "line": 357,
              "column": 3
            },
            {
              "title": "inline SVG visual rendered on nest screen after compute",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1408,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:27.083Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-1535e75418eb1ef8e7eb",
              "file": "printing.spec.js",
              "line": 363,
              "column": 3
            },
            {
              "title": "2-lane nesting: 2+3ft items on 5ft roll show savings in summary",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1591,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:28.515Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-d50109f81859a164bbe5",
              "file": "printing.spec.js",
              "line": 370,
              "column": 3
            },
            {
              "title": "2-lane: waste rectangle present in SVG for shorter lane",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1555,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:30.128Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-bbfb133f464368871f54",
              "file": "printing.spec.js",
              "line": 381,
              "column": 3
            },
            {
              "title": "linear group (same-width items) shows no lane savings",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1529,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:31.708Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-b3c0008d24b96e8d8ef7",
              "file": "printing.spec.js",
              "line": 392,
              "column": 3
            },
            {
              "title": "hover popup present in DOM",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1433,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:33.255Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-545e7fdfc45fbf178d83",
              "file": "printing.spec.js",
              "line": 402,
              "column": 3
            },
            {
              "title": "text plan toggle button shows/hides details",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1468,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:34.708Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-093260cd31123b5f69b0",
              "file": "printing.spec.js",
              "line": 409,
              "column": 3
            },
            {
              "title": "demo items produce 2-lane nesting for both Flex and Vinyl",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1248,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:36.205Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-6d30c45862d58ba37083",
              "file": "printing.spec.js",
              "line": 420,
              "column": 3
            },
            {
              "title": "demo button loads 4 items",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1221,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:07.128Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e01bd768b2389a308143",
              "file": "printing.spec.js",
              "line": 357,
              "column": 3
            },
            {
              "title": "inline SVG visual rendered on nest screen after compute",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1292,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:08.377Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-0ca335967ff9529d5547",
              "file": "printing.spec.js",
              "line": 363,
              "column": 3
            },
            {
              "title": "2-lane nesting: 2+3ft items on 5ft roll show savings in summary",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1525,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:09.687Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-e5ed090ec3c7fbef93cf",
              "file": "printing.spec.js",
              "line": 370,
              "column": 3
            },
            {
              "title": "2-lane: waste rectangle present in SVG for shorter lane",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1483,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:11.241Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-a3c1f9fda5e8449d76d9",
              "file": "printing.spec.js",
              "line": 381,
              "column": 3
            },
            {
              "title": "linear group (same-width items) shows no lane savings",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1608,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:12.741Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-6d46fcb4997f61ac8173",
              "file": "printing.spec.js",
              "line": 392,
              "column": 3
            },
            {
              "title": "hover popup present in DOM",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1363,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:14.372Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-f18abd3cf660ab61f5bb",
              "file": "printing.spec.js",
              "line": 402,
              "column": 3
            },
            {
              "title": "text plan toggle button shows/hides details",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1430,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:15.754Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-028251705e43e78d29c5",
              "file": "printing.spec.js",
              "line": 409,
              "column": 3
            },
            {
              "title": "demo items produce 2-lane nesting for both Flex and Vinyl",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1242,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:17.212Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-a7e7ae131d8b42c53bce",
              "file": "printing.spec.js",
              "line": 420,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Runs Screen",
          "file": "printing.spec.js",
          "line": 434,
          "column": 6,
          "specs": [
            {
              "title": "runs screen renders (empty or list)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3140,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:37.467Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-3b25031229719607eb5b",
              "file": "printing.spec.js",
              "line": 443,
              "column": 3
            },
            {
              "title": "filter bar shows all 5 filter buttons",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1174,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:40.633Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-56b7969aaceb925add18",
              "file": "printing.spec.js",
              "line": 450,
              "column": 3
            },
            {
              "title": "\"All\" filter is active by default",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1142,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:41.834Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-c9cb08940ea8b6185118",
              "file": "printing.spec.js",
              "line": 460,
              "column": 3
            },
            {
              "title": "clicking a filter button makes it active and deactivates others",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1229,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:42.994Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-435850aac22d8ef6d082",
              "file": "printing.spec.js",
              "line": 464,
              "column": 3
            },
            {
              "title": "clicking Planned filter reloads runs-body",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3197,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:44.247Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-27b361306b2c7060ae14",
              "file": "printing.spec.js",
              "line": 471,
              "column": 3
            },
            {
              "title": "runs screen renders (empty or list)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3107,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:18.471Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-7abf91c4d08d19b26e24",
              "file": "printing.spec.js",
              "line": 443,
              "column": 3
            },
            {
              "title": "filter bar shows all 5 filter buttons",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1139,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:21.593Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-6103064ec5e5d704465f",
              "file": "printing.spec.js",
              "line": 450,
              "column": 3
            },
            {
              "title": "\"All\" filter is active by default",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1139,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:22.751Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-47f2c1728fbdc72349f8",
              "file": "printing.spec.js",
              "line": 460,
              "column": 3
            },
            {
              "title": "clicking a filter button makes it active and deactivates others",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1158,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:23.912Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-2429fa665e9ea707305d",
              "file": "printing.spec.js",
              "line": 464,
              "column": 3
            },
            {
              "title": "clicking Planned filter reloads runs-body",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 3151,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:25.086Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-4ed10d688b2cc21447f5",
              "file": "printing.spec.js",
              "line": 471,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Cancel Run UI",
          "file": "printing.spec.js",
          "line": 481,
          "column": 6,
          "specs": [
            {
              "title": "cancel button element exists in run modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1092,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:47.459Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-8a867334f327fe45ec88",
              "file": "printing.spec.js",
              "line": 489,
              "column": 3
            },
            {
              "title": "cancel button starts hidden when modal is closed",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1100,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:48.565Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-ab43af209b59593bf292",
              "file": "printing.spec.js",
              "line": 494,
              "column": 3
            },
            {
              "title": "cancel button shown for planned run, hidden for terminal status",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4188,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:49.687Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-5fdd7cfaffff81cfc942",
              "file": "printing.spec.js",
              "line": 499,
              "column": 3
            },
            {
              "title": "cancel button element exists in run modal",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1060,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:28.255Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-749e66b94839ef221205",
              "file": "printing.spec.js",
              "line": 489,
              "column": 3
            },
            {
              "title": "cancel button starts hidden when modal is closed",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1083,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:29.333Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-3250c4f16befeecadab2",
              "file": "printing.spec.js",
              "line": 494,
              "column": 3
            },
            {
              "title": "cancel button shown for planned run, hidden for terminal status",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4212,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:30.435Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-081a53b0845897d33401",
              "file": "printing.spec.js",
              "line": 499,
              "column": 3
            }
          ]
        },
        {
          "title": "Print Nest — Stock Screen",
          "file": "printing.spec.js",
          "line": 517,
          "column": 6,
          "specs": [
            {
              "title": "stock screen loads without error",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4140,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:53.894Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-13941d521046b534329f",
              "file": "printing.spec.js",
              "line": 526,
              "column": 3
            },
            {
              "title": "stock cards render with name and quantity",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4330,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:41:58.059Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-b8e987c94e632bdbfbb1",
              "file": "printing.spec.js",
              "line": 533,
              "column": 3
            },
            {
              "title": "stock screen loads without error",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4116,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:34.667Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-37ecc299bb497c26af4f",
              "file": "printing.spec.js",
              "line": 526,
              "column": 3
            },
            {
              "title": "stock cards render with name and quantity",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4120,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:38.799Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "84f3bc84c047e9f9c0b8-b46fc8519fc0281dc3ee",
              "file": "printing.spec.js",
              "line": 533,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "production.spec.js",
      "file": "production.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Production — Smoke",
          "file": "production.spec.js",
          "line": 9,
          "column": 6,
          "specs": [
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1847,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:02.448Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-08ab67d2b70bac9026c0",
              "file": "production.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 275,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:04.326Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-16d4c3afa38c0afcaa8a",
              "file": "production.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 208,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:04.632Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-479a33c51e1e0ed8ae0d",
              "file": "production.spec.js",
              "line": 22,
              "column": 3
            },
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1219,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:42.956Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-a5a5a553947fd21ca24d",
              "file": "production.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 168,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:44.191Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-629b90f96f7227a10615",
              "file": "production.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js is versioned",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 182,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:44.376Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-e071e46e4761707c8728",
              "file": "production.spec.js",
              "line": 22,
              "column": 3
            }
          ]
        },
        {
          "title": "Production — HTML Structure",
          "file": "production.spec.js",
          "line": 29,
          "column": 6,
          "specs": [
            {
              "title": "all screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:04.869Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-751b1956f8285092c40a",
              "file": "production.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "hub auth gate and navigation present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:04.923Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-ff61111594cce2e575f7",
              "file": "production.spec.js",
              "line": 47,
              "column": 3
            },
            {
              "title": "all screens present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 4,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:44.573Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-aef8b5e752aa2910905d",
              "file": "production.spec.js",
              "line": 39,
              "column": 3
            },
            {
              "title": "hub auth gate and navigation present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 2,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:44.608Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "e03a4b7a6378dd18a075-6f38512f94362937af87",
              "file": "production.spec.js",
              "line": 47,
              "column": 3
            }
          ]
        }
      ]
    },
    {
      "title": "vehicle.spec.js",
      "file": "vehicle.spec.js",
      "column": 0,
      "line": 0,
      "specs": [],
      "suites": [
        {
          "title": "Vehicle Log — Smoke",
          "file": "vehicle.spec.js",
          "line": 9,
          "column": 6,
          "specs": [
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1541,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:04.954Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-c69be7604bfc5bd3eecf",
              "file": "vehicle.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 191,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:06.526Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-0af762c479f72f5c2368",
              "file": "vehicle.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js contains current vehicle version",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 203,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:06.733Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-4c6f0c5164abc99757d6",
              "file": "vehicle.spec.js",
              "line": 22,
              "column": 3
            },
            {
              "title": "page or hub loads",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 1280,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:44.622Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-f19cecc0003599b5d468",
              "file": "vehicle.spec.js",
              "line": 10,
              "column": 3
            },
            {
              "title": "manifest.json returns 200",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 157,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:45.927Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-6d3329d2335f2eb6f7a3",
              "file": "vehicle.spec.js",
              "line": 17,
              "column": 3
            },
            {
              "title": "sw.js contains current vehicle version",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 169,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.108Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-b4f1974d25bcf1fbe34d",
              "file": "vehicle.spec.js",
              "line": 22,
              "column": 3
            }
          ]
        },
        {
          "title": "Vehicle Log — HTML Structure (v18)",
          "file": "vehicle.spec.js",
          "line": 29,
          "column": 6,
          "specs": [
            {
              "title": "edit+delegate modals and edit-log screen present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 22,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:06.952Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-61ff6582f0f0b6314fd2",
              "file": "vehicle.spec.js",
              "line": 30,
              "column": 3
            },
            {
              "title": "canBypassPhoto and canEditEntry functions present in JS",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:06.987Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-e3848d43d66a83c9c8de",
              "file": "vehicle.spec.js",
              "line": 50,
              "column": 3
            },
            {
              "title": "photo bypass guards present (4 locations)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 14,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.015Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-9b6dda5ff14592fbe0ed",
              "file": "vehicle.spec.js",
              "line": 64,
              "column": 3
            },
            {
              "title": "edit+delegate modals and edit-log screen present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.298Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-eecfce8c5bbdcfc30b12",
              "file": "vehicle.spec.js",
              "line": 30,
              "column": 3
            },
            {
              "title": "canBypassPhoto and canEditEntry functions present in JS",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 12,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.323Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-1da678b2208dbba484d5",
              "file": "vehicle.spec.js",
              "line": 50,
              "column": 3
            },
            {
              "title": "photo bypass guards present (4 locations)",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 9,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.344Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-d807e8ea32c68934afb9",
              "file": "vehicle.spec.js",
              "line": 64,
              "column": 3
            }
          ]
        },
        {
          "title": "Vehicle Log — HTML Structure (v22)",
          "file": "vehicle.spec.js",
          "line": 76,
          "column": 6,
          "specs": [
            {
              "title": "mileage segment functions and retro-switch present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.039Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-e81fe7704c34b9bba4d8",
              "file": "vehicle.spec.js",
              "line": 77,
              "column": 3
            },
            {
              "title": "mileage segment functions and retro-switch present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 16,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.364Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-4ad5358a0f338a97379f",
              "file": "vehicle.spec.js",
              "line": 77,
              "column": 3
            }
          ]
        },
        {
          "title": "Vehicle Log — HTML Structure (v25)",
          "file": "vehicle.spec.js",
          "line": 93,
          "column": 6,
          "specs": [
            {
              "title": "fuel bill photo and CC slip photo elements present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.068Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-4ccd65b60ead57af1fd6",
              "file": "vehicle.spec.js",
              "line": 94,
              "column": 3
            },
            {
              "title": "fuel bill and CC slip handler functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 17,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.100Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-6be3e9e521885024edcc",
              "file": "vehicle.spec.js",
              "line": 108,
              "column": 3
            },
            {
              "title": "fuel bill photo and CC slip photo elements present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.390Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-ce93d65c65ac5105f143",
              "file": "vehicle.spec.js",
              "line": 94,
              "column": 3
            },
            {
              "title": "fuel bill and CC slip handler functions present",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 13,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.413Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-c8df628ad737ecf34fd7",
              "file": "vehicle.spec.js",
              "line": 108,
              "column": 3
            }
          ]
        },
        {
          "title": "Vehicle Log — Hub Navigation",
          "file": "vehicle.spec.js",
          "line": 122,
          "column": 6,
          "specs": [
            {
              "title": "dashboard header has Hub home button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 15,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.129Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-c65046058d915619ea54",
              "file": "vehicle.spec.js",
              "line": 123,
              "column": 3
            },
            {
              "title": "sw.js version is v30 or later",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "android-chrome",
                  "projectName": "android-chrome",
                  "results": [
                    {
                      "workerIndex": 8,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 7,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:42:07.157Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-6f06cad994967ee91b8e",
              "file": "vehicle.spec.js",
              "line": 132,
              "column": 3
            },
            {
              "title": "dashboard header has Hub home button",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 11,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.437Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-f4a712a3829d53a9aed5",
              "file": "vehicle.spec.js",
              "line": 123,
              "column": 3
            },
            {
              "title": "sw.js version is v30 or later",
              "ok": true,
              "tags": [],
              "tests": [
                {
                  "timeout": 30000,
                  "annotations": [],
                  "expectedStatus": "passed",
                  "projectId": "desktop-chrome",
                  "projectName": "desktop-chrome",
                  "results": [
                    {
                      "workerIndex": 17,
                      "parallelIndex": 0,
                      "status": "passed",
                      "duration": 10,
                      "errors": [],
                      "stdout": [],
                      "stderr": [],
                      "retry": 0,
                      "startTime": "2026-07-12T13:43:46.458Z",
                      "annotations": [],
                      "attachments": []
                    }
                  ],
                  "status": "expected"
                }
              ],
              "id": "084e7535606ab6e45f08-a6266891ef4a68c85905",
              "file": "vehicle.spec.js",
              "line": 132,
              "column": 3
            }
          ]
        }
      ]
    }
  ],
  "errors": [],
  "stats": {
    "startTime": "2026-07-12T13:40:24.630Z",
    "duration": 201939.26200000002,
    "expected": 228,
    "skipped": 0,
    "unexpected": 8,
    "flaky": 0
  }
}
