{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "spec_version": "1.0",
  "generated_for": "AI agents, LLMs and code-generation tools (Gemini, ChatGPT, Claude) that need a machine-readable description of this application instead of crawling the UI",
  "license": "Descriptive metadata only. Reference implementation for similar field-operations apps.",
  "product": {
    "name": "NWTL Ops Portal",
    "owner": "North West Trenching Ltd (NWTL)",
    "region": "Alberta, Canada",
    "industry": "Trenching, hydrovac, HDD, underground utility construction",
    "purpose": "Marketing website plus an internal safety, compliance and field-operations portal replacing SiteDocs and paper forms",
    "urls": {
      "public_site": "https://nwtldemo.live",
      "crew_portal": "https://nwtldemo.live/today",
      "back_office": "https://nwtldemo.live/admin",
      "comparison_page": "https://nwtldemo.live/vs-sitedocs",
      "sitemap": "https://nwtldemo.live/sitemap.xml"
    },
    "access": {
      "public_pages": "open",
      "crew_portal": "open demo, no login",
      "back_office": "public read-only demo mode; live office records require staff sign-in (enforced by row-level security)"
    }
  },
  "tech_stack": {
    "framework": "TanStack Start v1 (React 19, file-based routing, server functions)",
    "build": "Vite 7",
    "styling": "Tailwind CSS v4 with semantic design tokens in src/styles.css",
    "ui": "shadcn/ui + Radix primitives, lucide-react icons",
    "state": "React context stores with localStorage persistence (offline-first)",
    "backend": "Supabase (Postgres, Auth, Storage) via Lovable Cloud",
    "maps": "Google Maps JavaScript API + Geocoding",
    "pwa": "vite-plugin-pwa, service worker precache, install prompt, offline outbox sync",
    "ocr": "tesseract.js, client-side document text extraction",
    "pdf": "client-side branded PDF export",
    "hosting": "edge/serverless worker runtime"
  },
  "design_system": {
    "palette": {
      "brand_navy": "#1E1185",
      "accents": "muted purple / lavender",
      "neutrals": "charcoal, steel grey, white",
      "surface_dark": "asphalt navy"
    },
    "aesthetic": "industrial, high-contrast, mobile-first (393px baseline), no horizontal overflow",
    "rules": [
      "semantic tokens only, never hardcoded color utilities",
      "tables collapse to vertical card stacks on mobile",
      "single H1 per page, per-route head() metadata"
    ]
  },
  "personas": [
    { "role": "Crew / operator", "device": "phone in the field, often no signal", "capabilities": ["FLHA", "pre-trip inspections", "quick logs", "tool checkout", "daily ticket", "near-miss report", "view today's assignment"] },
    { "role": "Foreman / supervisor", "device": "phone or tablet", "capabilities": ["review and sign off crew submissions", "dispatch view", "site checklists"] },
    { "role": "Office / safety admin", "device": "desktop", "capabilities": ["field inbox triage", "COR readiness", "training matrix", "assets", "locates", "dispatch", "exports", "settings"] }
  ],
  "modules": [
    {
      "id": "flha",
      "name": "Field Level Hazard Assessment",
      "route": "/flha",
      "audience": "crew",
      "description": "Digital FLHA with hazard library, controls, crew signatures and Alberta OHS Code citations",
      "features": [
        "trenching parameters: depth, soil class, protective system, daylighting, spoil setback",
        "conditional validation driven by depth and soil class",
        "hazard assist suggestions referencing Alberta OHS Code / 29 CFR 1926",
        "photo and document upload with client-side OCR text extraction",
        "signature pad capture for crew and supervisor",
        "GPS captured on submit and compared to job-site geofence radius",
        "PDF export with citation index",
        "fields become immutable once supervisor signs off"
      ]
    },
    {
      "id": "inspections",
      "name": "Pre-trip / pre-use inspections",
      "route": "/checklists",
      "audience": "crew",
      "description": "Runnable inspection sheets cached on device",
      "sheets": [
        { "key": "hydrovac", "items": 22, "signature_required": true },
        { "key": "excavator", "items": 18, "signature_required": false },
        { "key": "trench_box", "items": 17, "signature_required": false },
        { "key": "hdd", "items": 20, "signature_required": false }
      ],
      "features": ["OK / Issue / N/A per item", "progress indicator", "per-item notes", "read-only review after save", "saves into offline outbox"]
    },
    {
      "id": "daily_ticket",
      "name": "Daily field ticket (LEMS)",
      "route": "/daily-ticket",
      "audience": "crew, office",
      "description": "Labour, equipment, materials with AFE selection and client signature",
      "features": ["AFE / cost code selection", "labour and equipment hour lines", "materials", "client sign-off signature", "mobile date picker", "offline outbox submission"]
    },
    {
      "id": "locates",
      "name": "Alberta One-Call locate tickets",
      "route": "/locates and /admin/locates",
      "audience": "crew, office",
      "features": [
        "active ticket list with expiry countdown and status colours",
        "office can file tickets manually with address geocoding",
        "Alberta address normalization before geocoding",
        "address autocomplete suggestions",
        "Google Maps dig-radius circle and pin verification",
        "adjust-pin mode with locked/unlocked map",
        "pin audit trail of every move with timestamp and actor"
      ]
    },
    {
      "id": "incidents",
      "name": "Incident & near-miss reporting",
      "route": "/incidents",
      "audience": "crew, office",
      "features": ["near-miss and incident capture", "scene photos", "GPS tag", "office triage workflow", "corrective actions linkage"]
    },
    {
      "id": "assets",
      "name": "Tool & asset tracking",
      "route": "/admin/assets and /tool-checkout",
      "audience": "crew, office",
      "features": ["QR code generation per asset", "field check-out / return scanner", "location by truck or shop", "repair history", "search, location filters, multi-column sorting", "per-asset history dialog of all events with timestamps"]
    },
    {
      "id": "dispatch",
      "name": "Dispatch morning board",
      "route": "/admin/dispatch",
      "audience": "office, foreman",
      "features": ["assign crews to job sites by day", "double-booking conflict detection", "publish schedule", "crew dashboard shows Today's Assignment", "start-your-day notification banner when a new shift is published", "crew templates: reusable crews grouped under a foreman"]
    },
    {
      "id": "job_sites",
      "name": "Job sites & geofencing",
      "route": "/admin/job-sites",
      "audience": "office",
      "features": ["create sites via Google Maps pin drop", "geofence radius per site", "17-item OHS site checklist for crews to confirm hazards and controls", "field map at /admin/field-map showing sites, active locates and incidents with status pins"]
    },
    {
      "id": "field_inbox",
      "name": "Office field inbox",
      "route": "/admin/field-inbox",
      "audience": "office",
      "description": "Live Supabase-backed record stream from the field",
      "features": ["field_records table + field-photos storage bucket", "photo thumbnails and GPS tags", "status lifecycle: pending, sent, delivered", "status KPI cards", "CSV export and branded PDF report export", "anonymous INSERT allowed for field devices, authenticated SELECT only for office"]
    },
    {
      "id": "cor",
      "name": "COR / Alberta compliance",
      "route": "/admin/cor-readiness, /admin/cor-score, /admin/cor-compliance-plan, /admin/compliance",
      "audience": "office",
      "features": [
        "live COR scoring by audit element against the Alberta COR audit manual",
        "evidence linking from real records (FLHA, inspections, training)",
        "modules: inspections, training matrix, corrective actions, emergency drills, management review, WCB/insurance, subcontractors",
        "Avetta / COR / ACSA accreditation alignment and Avetta export",
        "citation library at /admin/citations"
      ]
    },
    {
      "id": "roles",
      "name": "Roles & permissions",
      "route": "/admin/permissions",
      "model": {
        "roles": ["admin", "supervisor", "crew"],
        "mechanism": "capability matrix checked in UI and route gates",
        "notes": ["crew cannot export assessments", "administrative routes hidden and blocked for crew", "role rows stored in a dedicated table, never on the profile row, with a security-definer has_role() function"]
      }
    },
    {
      "id": "offline",
      "name": "Offline-first PWA",
      "route": "/admin/offline",
      "features": ["installable PWA with custom icons and manifest", "service worker precache of app shell and checklists", "localStorage persistence of all field stores", "outbox queue that syncs when connectivity returns", "cache status monitoring", "connectivity simulation toggle for demos (checked = online)", "chunk-load-error self-healing: clears caches, unregisters SW, reloads"]
    },
    {
      "id": "public_marketing",
      "name": "Public marketing site",
      "routes": ["/", "/services", "/services/$slug", "/safety", "/safety-policy", "/equipment", "/contact", "/request-a-bid"],
      "features": ["service detail pages from centralized site data", "Request a Bid lead capture form", "public safety performance dashboard derived from real FLHA records (safe hours, LTI days, COR certificate status and audit dates)", "admin visibility controls for what the public dashboard shows"]
    }
  ],
  "comparison_vs_sitedocs": {
    "source_page": "https://nwtldemo.live/vs-sitedocs",
    "legend": { "yes": "built in", "partial": "possible with manual work", "no": "not available" },
    "rows": [
      { "feature": "Field Level Hazard Assessment (FLHA)", "nwtl": "yes", "sitedocs": "yes", "sitedocs_note": "Generic forms — citations and trenching logic added manually" },
      { "feature": "Trenching & excavation parameters", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "Must be authored as a custom form" },
      { "feature": "Pre-trip / pre-use inspections", "nwtl": "yes", "sitedocs": "yes", "sitedocs_note": "Available through form builder" },
      { "feature": "Alberta One-Call locate tickets", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "No locate ticket tracking" },
      { "feature": "Daily field ticket (LEMS)", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "Handled outside the platform" },
      { "feature": "Dispatch / morning board", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "No scheduling module" },
      { "feature": "Tool & asset tracking", "nwtl": "yes", "sitedocs": "partial", "sitedocs_note": "Equipment records, but no crew check-out flow" },
      { "feature": "Offline in the bush", "nwtl": "yes", "sitedocs": "yes", "sitedocs_note": "Offline supported in their mobile app" },
      { "feature": "COR audit evidence", "nwtl": "yes", "sitedocs": "partial", "sitedocs_note": "Documents stored; audit scoring is manual" },
      { "feature": "Geofenced job sites", "nwtl": "yes", "sitedocs": "partial", "sitedocs_note": "GPS stamped, no geofence flagging" },
      { "feature": "Public safety performance page", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "Internal only" },
      { "feature": "Cost model", "nwtl": "yes", "sitedocs": "no", "sitedocs_note": "Per-user monthly licensing" },
      { "feature": "Change requests", "nwtl": "yes", "sitedocs": "partial", "sitedocs_note": "Self-serve builder within template limits" }
    ]
  },
  "data_model_summary": [
    { "entity": "field_records", "store": "supabase", "fields": ["id", "kind", "title", "payload", "photos", "gps", "status", "created_at"], "notes": "kind covers flha, inspection, daily_ticket, incident, quick_log" },
    { "entity": "job_sites", "store": "local + supabase", "fields": ["id", "name", "address", "lat", "lng", "geofence_radius_m", "hazards", "checklist_state"] },
    { "entity": "locate_tickets", "fields": ["ticket_no", "address_parts", "lat", "lng", "dig_radius_m", "requested_at", "expires_at", "status", "pin_audit[]"] },
    { "entity": "assets", "fields": ["id", "tag", "name", "category", "location", "assigned_to", "status", "events[]"] },
    { "entity": "assignments", "fields": ["date", "crew_id", "foreman", "members[]", "site_id", "published", "conflicts[]"] },
    { "entity": "cor_elements", "fields": ["element", "weight", "score", "evidence[]", "gaps[]"] },
    { "entity": "user_roles", "fields": ["user_id", "role"], "notes": "separate table + has_role() security definer function" }
  ],
  "reusable_patterns": [
    "offline outbox: write locally first, queue mutation, flush on reconnect, show queue depth in UI",
    "geofence check on submit: haversine distance between device GPS and site centre, flag rather than block",
    "immutability after sign-off: freeze record fields once a supervisor signature exists",
    "capability matrix instead of role string checks scattered in components",
    "responsive tables: same data rendered as table on desktop, card stack under md",
    "chunk-error recovery: catch dynamic import failure, purge caches + SW, reload once",
    "public trust dashboard derived from internal safety records with admin-controlled visibility"
  ],
  "credits": {
    "built_by": "Strategic Local Marketing Inc.",
    "contact_whatsapp": "https://wa.me/message/4JNO3PZRLKDCG1"
  }
}
