// Documentation / Wiki — keyword index, articles, release notes

const DOC_CATEGORIES = [
  { id: "getting-started", label: "Getting Started", icon: "book" },
  { id: "modules",         label: "Modules",         icon: "grid" },
  { id: "roles",           label: "Roles & Access",  icon: "shield" },
  { id: "workflows",       label: "Workflows",       icon: "arrowRight" },
  { id: "admin",           label: "Admin Console",   icon: "settings" },
  { id: "faq",             label: "FAQ",             icon: "chat" },
  { id: "releases",        label: "Release Notes",   icon: "history" },
];

const DOC_ARTICLES = [
  // -------- Getting Started --------
  {
    id: "gs-overview", category: "getting-started", title: "Welcome to TCS",
    tags: ["overview", "tcs", "ocean procurement", "apa", "terminal cost simulation"],
    updated: "2026-05-24", author: "Ocean Procurement",
    excerpt: "What TCS is, who it's for, and how the modules fit together.",
    blocks: [
      { type: "p", text: "TCS (Terminal Cost Simulation System) is the single source of truth for APA terminal contracts and rates, built on CYB and CYM data from Rateflow. It provides Ocean Procurement teams with accurate, consistent rate data to support cost simulations, planning, and operational decision-making." },
      { type: "callout", tone: "info", title: "Available modules", text: "Rate Repository · Rate Health · ROE Table · Rate Calculator · Access Requests · Admin Console (Users · Permissions · Metadata · Coverage Matrix · Approval Routing)." },
      { type: "h2", text: "Who uses TCS" },
      { type: "ul", items: [
        "Rate Focals maintain terminal rate data, create and renew rates, and manage expiry schedules.",
        "Finance / FBP teams use the Rate Calculator to simulate terminal costs and analyse scenarios.",
        "Procurement managers review and approve access requests for their regions.",
        "Admins configure metadata, ROE, coverage matrix, approval routing, and manage users.",
      ]},
      { type: "h2", text: "Key concepts" },
      { type: "kv", rows: [
        ["Rate", "A per-unit cost charged by a terminal, tied to a cost type, equipment type, and effective date range."],
        ["ROE", "Rate of Exchange — converts local currency rates to USD for cost simulation."],
        ["Coverage Matrix", "Defines which terminal × cost type combinations are expected to have active rates, driving the completeness check in Rate Health."],
        ["QV Rules", "Quality Validation rules (QV-01 to QV-13) that automatically scan active rates for data issues."],
        ["Approval Routing", "Configuration that determines which approver receives access requests for each region."],
      ]},
    ],
    related: ["gs-quickstart", "mod-rates", "mod-health", "role-matrix"],
  },
  {
    id: "gs-quickstart", category: "getting-started", title: "Quickstart by role",
    tags: ["quickstart", "tutorial", "roles", "first time", "getting started"],
    updated: "2026-05-24",
    excerpt: "First-day instructions for each role in TCS.",
    blocks: [
      { type: "p", text: "First time signing in? Find your role below and follow the steps to get started." },
      { type: "h3", text: "Rate Focal (Write access)" },
      { type: "ol", items: [
        "Confirm your region/area scope shown in the top bar.",
        "Open Rate Repository — review rates expiring soon (flagged in the Expiry column).",
        "Open Rate Health → Expiry Alerts to see the full expiry dashboard by time window.",
        "Add new rates via Add Rate (single) or Import (bulk Excel / Rateflow export).",
        "After saving, Quality Validation runs automatically — resolve any Errors before re-saving.",
      ]},
      { type: "h3", text: "Finance / FBP" },
      { type: "ol", items: [
        "Open Rate Calculator — select terminal, cost type, equipment, and quantity.",
        "Review the USD cost breakdown with the active ROE applied.",
        "Adjust inputs to run what-if scenarios (e.g. volume change, rate change).",
        "Use Rate Health → Completeness to confirm your terminal's coverage is up to date.",
      ]},
      { type: "h3", text: "New user (no access yet)" },
      { type: "ol", items: [
        "Click Access Requests in the left menu.",
        "Submit a new request — select the role and region you need and provide justification.",
        "Your region's approver will receive an email notification.",
        "On approval you'll be notified and access is granted immediately.",
      ]},
    ],
    related: ["mod-rates", "mod-health", "mod-calc", "wf-access"],
  },

  // -------- Modules --------
  {
    id: "mod-rates", category: "modules", title: "Rate Repository",
    tags: ["rates", "repository", "cost types", "import", "export", "batch edit", "filter", "rateflow"],
    updated: "2026-05-24",
    excerpt: "Browse, create, edit, import and export all terminal rates.",
    blocks: [
      { type: "p", text: "The Rate Repository is the central store for all terminal cost rates. Each rate record captures: terminal, cost type, equipment type, container size, container fullness, shipment type, rate (local currency), currency, effective date, expiry date, slab configuration, brand company, corridor, contract owner, and full audit metadata." },
      { type: "h2", text: "Browsing and filtering" },
      { type: "ul", items: [
        "Filter by region, terminal, cost type, equipment type, rate status, and effective date range using the filter bar.",
        "Sort any column by clicking its header.",
        "Click a row to open the rate detail drawer with full field values and change history.",
        "Local rate and approximate USD equivalent are shown side by side using the active ROE.",
        "The status badge shows Active, Draft, Inactive, or Schedule.",
      ]},
      { type: "h2", text: "Adding a single rate" },
      { type: "p", text: "Click Add Rate to open the rate form. Required fields are marked with an asterisk. On save, Quality Validation runs automatically — Errors must be resolved, Warnings can be acknowledged." },
      { type: "h2", text: "Batch edit" },
      { type: "p", text: "Select multiple rows using the checkbox column, then click Batch Edit. You can update shared fields (e.g. expiry date, contract owner, status) across all selected rates in one action." },
      { type: "h2", text: "Importing rates" },
      { type: "table", headers: ["Method", "Use when", "Format"], rows: [
        ["Excel Template", "Bulk entry — download the pre-filled template, fill rows, upload and validate.", ".xlsx / .csv"],
        ["Rateflow Raw Data", "Direct export from Rateflow (CYB / CYM). Columns are auto-mapped against OCMS metadata.", ".xlsx (Rateflow v2 / v3)"],
      ]},
      { type: "h2", text: "Exporting" },
      { type: "p", text: "Click Export in the top-right corner to download the current filtered view as an Excel file. The export respects your active filters and regional scope." },
      { type: "callout", tone: "warn", title: "Regional scope",
        text: "You can only create or edit rates for terminals within your assigned region/area. Rates outside your scope are read-only." },
    ],
    related: ["mod-health", "mod-calc", "wf-import"],
  },
  {
    id: "mod-health", category: "modules", title: "Rate Health Dashboard",
    tags: ["health", "completeness", "expiry", "validation", "quality", "qv", "coverage matrix"],
    updated: "2026-05-24",
    excerpt: "Real-time data quality view — completeness, expiry alerts, and quality validation.",
    blocks: [
      { type: "p", text: "Rate Health gives a real-time view of the health of your rate dataset across three dimensions: coverage completeness, upcoming expiries, and data quality rule violations." },
      { type: "h3", text: "Completeness" },
      { type: "p", text: "Compares active rates against the Coverage Matrix — the configured set of terminal × cost type combinations that should have rates. Each terminal is shown as a card with a completeness ring (Present / Expiring / Missing). Click a terminal card to filter the table below to that terminal only." },
      { type: "h3", text: "Expiry Alerts" },
      { type: "p", text: "Lists Active rates with an expiry date approaching. Use the 30 / 60 / 90-day window toggle to adjust the look-ahead period. Colour coding: red ≤ 14 days, amber ≤ 30 days, yellow ≤ 60 days. The Expired section shows rates past their expiry date that have no successor rate." },
      { type: "h3", text: "Quality Validation" },
      { type: "p", text: "Runs 13 validation rules (QV-01 to QV-13) across all Active rates on demand. Click Run Validation to refresh results. Errors indicate data integrity problems; Warnings indicate best-practice deviations." },
      { type: "kv", rows: [
        ["QV-01", "Duplicate record — two rates share identical business fields (Error)"],
        ["QV-04", "Expiry before effective — expiry_date < effective_date (Error)"],
        ["QV-05", "Zero / negative rate — rate_local ≤ 0 (Warning)"],
        ["QV-06", "Slab range invalid — slab_min > slab_max, or slab values are 0 (Error / Warning)"],
        ["QV-07", "Missing ROE — non-USD rate has no active exchange rate entry (Warning)"],
        ["QV-08", "Required field missing — a dimension field required by the cost type is NULL (Configurable)"],
        ["QV-09", "Container size rate order — rate for 45ft < 40ft or 40ft < 20ft (Warning)"],
        ["QV-10", "Container type rate order — Dry rate exceeds a Reefer / DG / OOG rate in the same group (Warning)"],
        ["QV-11", "Duplicate STCY flag — more than one rate has stcy_flag = true in the same group (Error)"],
        ["QV-12", "Duplicate invoice rate flag — more than one rate has is_invoicerate = true in the same group (Error)"],
        ["QV-13", "Duplicate calculator flag — more than one rate has calculator = true in the same group (Error)"],
      ]},
      { type: "callout", tone: "info", title: "Validation results are cached",
        text: "Results are cached for 60 seconds on the server. Click Run Validation to force a fresh scan." },
    ],
    related: ["mod-rates", "admin-coverage", "role-matrix"],
  },
  {
    id: "mod-roe", category: "modules", title: "ROE Table",
    tags: ["roe", "exchange rate", "currency", "usd", "local currency"],
    updated: "2026-05-24",
    excerpt: "Manage currency exchange rates used in cost simulation.",
    blocks: [
      { type: "p", text: "The ROE (Rate of Exchange) Table stores the exchange rates used to convert local-currency rate values to USD in the Rate Calculator. Each ROE entry has a currency, an effective date, an optional expiry date, and a rate value (local per 1 USD)." },
      { type: "h2", text: "How ROE is applied" },
      { type: "ul", items: [
        "When the Rate Calculator computes a cost, it looks up the ROE record for the rate's currency that is active on the selected effective date.",
        "If no ROE is found for that currency and date, QV-07 will flag the rate as a warning in Rate Health.",
        "USD-denominated rates do not require an ROE entry.",
      ]},
      { type: "h2", text: "Managing ROE entries" },
      { type: "ul", items: [
        "Admin and Write roles can add new ROE entries.",
        "Set the effective date to the date the rate becomes active.",
        "If the rate has an end date, set the expiry date. Leave blank for open-ended rates.",
        "Multiple rates for the same currency with non-overlapping date ranges are supported.",
      ]},
      { type: "callout", tone: "warn", title: "Impact on Rate Health",
        text: "Adding or updating an ROE entry will resolve QV-07 warnings for affected rates on the next validation run." },
    ],
    related: ["mod-health", "mod-calc"],
  },
  {
    id: "mod-calc", category: "modules", title: "Rate Calculator",
    tags: ["calculator", "cost simulation", "usd", "slab", "tiered", "equipment", "effective date"],
    updated: "2026-05-24",
    excerpt: "Simulate terminal costs in USD based on active rates and the current ROE.",
    blocks: [
      { type: "p", text: "The Rate Calculator allows any user to estimate terminal costs in USD without accessing underlying rate sheets directly. Select terminal, cost type, equipment type, container size, quantity, and effective date — the system retrieves the matching active rate, applies the ROE, and returns the USD cost breakdown." },
      { type: "h2", text: "How it works" },
      { type: "ol", items: [
        "Select a terminal — filtered to your regional scope.",
        "Select a cost type — the form adapts to the cost type's required fields.",
        "Fill in equipment type, container size, container fullness, and quantity.",
        "Set the effective date — the system matches the rate active on that date.",
        "Click Calculate — results show the local rate, ROE applied, and USD total.",
      ]},
      { type: "h2", text: "Slab / tiered rates" },
      { type: "p", text: "For cost types with slab pricing, the calculator applies the correct slab bracket based on the quantity entered. Each slab tier is shown individually in the result breakdown." },
      { type: "callout", tone: "warn", title: "No rate found?" ,
        text: "If the result shows $0 or no rate, check: (1) Is there an Active rate for the selected combination on that date? (2) Is the ROE table populated for the rate's currency?" },
    ],
    related: ["mod-rates", "mod-roe", "mod-health"],
  },
  {
    id: "mod-access", category: "modules", title: "Access Requests",
    tags: ["access", "request", "approval", "role", "region", "permission"],
    updated: "2026-05-24",
    excerpt: "Submit and track role access requests — routed to the approver for your region.",
    blocks: [
      { type: "p", text: "Access Requests is the self-service portal for requesting TCS access. Requests are automatically routed to the configured approver for the relevant region. You can track the status of your submitted requests in real time." },
      { type: "h2", text: "Submitting a request" },
      { type: "ol", items: [
        "Click Access Requests in the left menu.",
        "Click New Request.",
        "Select the role you need (View, Write, or Admin) and the region(s) you require access to.",
        "Enter a business justification — this is sent to the approver.",
        "Submit. The designated approver for your region receives an email notification.",
      ]},
      { type: "h2", text: "Request statuses" },
      { type: "kv", rows: [
        ["Pending", "Awaiting approver action."],
        ["Approved", "Access has been granted. You can now log in with the new role."],
        ["Rejected", "Request was declined. The approver's comment is shown in the request detail."],
      ]},
      { type: "callout", tone: "info", title: "Approver routing",
        text: "Approvers are configured per region in Admin Console → Approval Routing. Contact your admin if your request is not being routed correctly." },
    ],
    related: ["wf-access", "admin-routing", "role-matrix"],
  },

  // -------- Roles --------
  {
    id: "role-matrix", category: "roles", title: "Permission matrix",
    tags: ["roles", "rbac", "permissions", "access", "view", "write", "admin"],
    updated: "2026-05-24",
    excerpt: "The full role × feature permission table.",
    blocks: [
      { type: "p", text: "TCS uses role-based access control (RBAC). Roles are assigned per user and scoped to a region/area. A user can hold different roles in different regions." },
      { type: "permMatrix" },
      { type: "h2", text: "Role descriptions" },
      { type: "kv", rows: [
        ["View", "Read-only access to Rate Repository, Rate Health, ROE Table, and Rate Calculator. Cannot create, edit, or delete any data."],
        ["Write", "Full read + create/edit/delete rates, ROE entries, and coverage matrix entries within their assigned region."],
        ["Admin", "Full system access including user management, metadata configuration, permissions, approval routing, and audit log."],
      ]},
      { type: "callout", tone: "warn", title: "Backend enforcement",
        text: "All API endpoints enforce role and regional scope checks server-side. The frontend hides controls for unavailable actions, but the API is the true security boundary." },
    ],
    related: ["mod-access", "wf-access", "admin-users"],
  },

  // -------- Workflows --------
  {
    id: "wf-import", category: "workflows", title: "Importing rates",
    tags: ["import", "excel", "rateflow", "template", "bulk", "upload"],
    updated: "2026-05-24",
    excerpt: "Step-by-step guide to bulk importing rates via Excel Template or Rateflow export.",
    blocks: [
      { type: "h3", text: "Option A — Excel Template" },
      { type: "ol", items: [
        "Open Rate Repository → Import.",
        "Select Excel Template and click Download template.",
        "Fill in the template — required columns are highlighted. Use the dropdown lists in the template for coded fields (terminal, cost type, equipment type, etc.).",
        "Upload the completed file.",
        "Review the Validate & Preview step — errors are shown per row.",
        "Fix any errors and re-upload, or deselect invalid rows.",
        "Click Confirm Import to write all valid rows to the rate table.",
      ]},
      { type: "h3", text: "Option B — Rateflow Raw Data" },
      { type: "ol", items: [
        "Export your rate sheet from Rateflow (v2 or v3 format).",
        "Open Rate Repository → Import → Rateflow Raw Data.",
        "Upload the Rateflow .xlsx file.",
        "Columns are auto-mapped against TCS metadata — review the mapping on the preview screen.",
        "Confirm import.",
      ]},
      { type: "callout", tone: "info", title: "After import",
        text: "Quality Validation (QV-01 to QV-13) runs automatically on all imported rates. Review Rate Health → Quality Validation to check for any rule violations in the imported data." },
    ],
    related: ["mod-rates", "mod-health"],
  },
  {
    id: "wf-access", category: "workflows", title: "Requesting and approving access",
    tags: ["access", "approval", "approver", "routing", "region", "workflow"],
    updated: "2026-05-24",
    excerpt: "End-to-end access request workflow — from submission to approval.",
    blocks: [
      { type: "h3", text: "For requesters" },
      { type: "ol", items: [
        "Open Access Requests → New Request.",
        "Select the role (View / Write / Admin) and region(s) you need.",
        "Enter a business justification — be specific about what you need to do.",
        "Submit. The approver for your region receives an email with the request details.",
        "Once approved or rejected, your request status updates in real time.",
      ]},
      { type: "h3", text: "For approvers" },
      { type: "ol", items: [
        "You will receive an email when a request is submitted for your region.",
        "Log into TCS and open Admin Console → Users.",
        "Locate the pending request — or open Access Requests to see all requests for your region.",
        "Click Approve or Reject. If rejecting, add a comment to help the requester understand why.",
      ]},
      { type: "callout", tone: "info", title: "Configuring approvers",
        text: "Approval routing is set per region in Admin Console → Approval Routing. Only admins can update routing." },
    ],
    related: ["mod-access", "admin-routing", "role-matrix"],
  },
  {
    id: "wf-expiry", category: "workflows", title: "Managing expiring rates",
    tags: ["expiry", "renewal", "expiry alerts", "rate health", "workflow"],
    updated: "2026-05-24",
    excerpt: "How to identify and renew rates approaching their expiry date.",
    blocks: [
      { type: "ol", items: [
        "Open Rate Health → Expiry Alerts.",
        "Use the 30 / 60 / 90-day window toggle to plan ahead.",
        "Click any expiring rate row to open the Rate Repository detail for that rate.",
        "Click Edit and update the expiry date (or create a new successor rate with the updated values).",
        "Save. Quality Validation runs — confirm no new violations are introduced.",
        "The rate will disappear from the Expiry Alerts list once the expiry date is updated.",
      ]},
      { type: "callout", tone: "warn", title: "Expired rates",
        text: "The Expired section in Expiry Alerts shows Active rates past their expiry date with no successor. These should be updated or inactivated promptly — they affect completeness scoring and may cause incorrect calculator results." },
    ],
    related: ["mod-health", "mod-rates"],
  },

  // -------- Admin Console --------
  {
    id: "admin-users", category: "admin", title: "Users",
    tags: ["users", "admin", "role assignment", "region", "scope", "access management"],
    updated: "2026-05-24",
    excerpt: "View and manage all TCS users, their roles and regional scope.",
    blocks: [
      { type: "p", text: "The Users tab in Admin Console provides a full list of all registered TCS users. Admins can view each user's assigned role, region/area scope, and account status." },
      { type: "h2", text: "What you can do" },
      { type: "ul", items: [
        "View all users and their current role and regional scope.",
        "Edit a user's role or region assignment directly.",
        "Deactivate a user to remove their access without deleting the account record.",
        "Review pending access requests and approve or reject them from this screen.",
      ]},
      { type: "callout", tone: "warn", title: "Admin only",
        text: "The Users tab is only visible to users with the Admin role." },
    ],
    related: ["role-matrix", "wf-access", "admin-perms"],
  },
  {
    id: "admin-perms", category: "admin", title: "Permissions",
    tags: ["permissions", "admin", "rbac", "role", "endpoint", "access control"],
    updated: "2026-05-24",
    excerpt: "Configure which roles can access which features and API endpoints.",
    blocks: [
      { type: "p", text: "The Permissions tab provides a view of the role-based access control configuration for TCS. It maps each module and API endpoint to the roles that are permitted to access it." },
      { type: "h2", text: "How permissions work" },
      { type: "ul", items: [
        "Permissions are enforced server-side on every API request — the frontend is not the security boundary.",
        "Each route is mapped to one or more allowed roles in the permissions configuration.",
        "A user must hold at least one of the allowed roles (within their regional scope) for the request to succeed.",
        "If no roles are configured for a route, any authenticated user can access it.",
      ]},
      { type: "callout", tone: "warn", title: "Changes take effect immediately",
        text: "Permissions are loaded and cached at startup. Contact the system administrator if a permission change does not take effect." },
    ],
    related: ["role-matrix", "admin-users"],
  },
  {
    id: "admin-metadata", category: "admin", title: "Metadata",
    tags: ["metadata", "admin", "terminals", "cost types", "equipment", "currency", "move type", "brand company"],
    updated: "2026-05-24",
    excerpt: "Manage the reference data tables used across all TCS modules.",
    blocks: [
      { type: "p", text: "The Metadata tab is the central configuration area for all reference / lookup tables used throughout TCS. Changes here flow through to all rate forms, filters, and calculators immediately." },
      { type: "h2", text: "Managed entities" },
      { type: "kv", rows: [
        ["Terminals", "Terminal code, name, region, area, and active flag. Used in all rate and health screens."],
        ["Cost Types", "Cost type code, name, active flag, required dimension columns (for QV-08), and QV-08 severity."],
        ["Equipment Types", "Container type codes (e.g. Dry, Reefer, DG, OOG) used in rate dimension fields."],
        ["Move Types", "Shipment type codes used in rate records and calculator."],
        ["Currencies", "ISO currency codes used in rate records and ROE entries."],
        ["Charge Units", "Unit codes (e.g. per TEU, per day) and their calculator display labels."],
        ["Slab Units", "Slab unit codes and their calculator display labels for tiered pricing."],
        ["Brand Companies", "Brand / company entities used in rate segmentation fields."],
        ["Regions / Areas", "Geographic hierarchy for regional scoping of user access."],
      ]},
      { type: "callout", tone: "warn", title: "Impact of deactivating",
        text: "Deactivating a terminal, cost type, or equipment type hides it from new rate creation forms but does not affect existing rate records." },
    ],
    related: ["admin-coverage", "mod-rates"],
  },
  {
    id: "admin-coverage", category: "admin", title: "Coverage Matrix",
    tags: ["coverage matrix", "admin", "completeness", "expected rates", "terminal", "cost type"],
    updated: "2026-05-24",
    excerpt: "Define which terminal × cost type combinations are expected to have active rates.",
    blocks: [
      { type: "p", text: "The Coverage Matrix defines the expected rate configuration — which terminal × cost type × equipment type × container size × fullness × shipment type × brand combinations should have Active rates. It drives the Completeness view in Rate Health." },
      { type: "h2", text: "How it works" },
      { type: "ul", items: [
        "Each row in the matrix is a required combination. A terminal card in Rate Health shows Missing (red) if no Active rate covers a required combination.",
        "Terminal-level rules apply to a specific terminal. Leave terminal blank to apply the rule to all terminals that have at least one rate of that cost type.",
        "Dimension fields (equipment type, container size, etc.) can be left blank to act as catch-all rules.",
      ]},
      { type: "h2", text: "Managing the matrix" },
      { type: "ul", items: [
        "Click Add Entry to define a new required combination manually.",
        "Click Auto-Populate to generate matrix entries from all distinct combinations of existing Active rates — useful for initial setup.",
        "Toggle the is_required flag on any entry to temporarily exclude it from the completeness check without deleting it.",
        "Use Bulk Delete to remove multiple entries at once.",
      ]},
      { type: "callout", tone: "info", title: "QV-08 — Required fields per cost type",
        text: "You can also configure which dimension fields (e.g. containertype, container_size) are mandatory for a given cost type in Metadata → Cost Types. This is separate from the Coverage Matrix and drives QV-08 validation." },
    ],
    related: ["mod-health", "admin-metadata"],
  },
  {
    id: "admin-routing", category: "admin", title: "Approval Routing",
    tags: ["approval routing", "admin", "approver", "region", "access request", "workflow"],
    updated: "2026-05-24",
    excerpt: "Configure which approver receives access requests for each region.",
    blocks: [
      { type: "p", text: "Approval Routing maps each region to one or more designated approvers. When a user submits an Access Request for a region, the approver(s) configured for that region receive an email notification and must take action." },
      { type: "h2", text: "Configuring routing" },
      { type: "ol", items: [
        "Open Admin Console → Approval Routing.",
        "Select a region from the list.",
        "Add or remove approvers by their name or email.",
        "Save. New access requests for that region will now route to the updated approver list.",
      ]},
      { type: "h2", text: "Multiple approvers" },
      { type: "p", text: "If multiple approvers are configured for a region, all of them receive the notification email. The first approver to act (approve or reject) closes the request — others do not need to respond." },
      { type: "callout", tone: "warn", title: "No approver configured",
        text: "If no approver is set for a region, access requests for that region will remain Pending indefinitely. Ensure every active region has at least one approver." },
    ],
    related: ["wf-access", "mod-access", "admin-users"],
  },

  // -------- FAQ --------
  {
    id: "faq-edit-rate", category: "faq", title: "Why can't I edit a rate?",
    tags: ["edit", "permission", "scope", "region", "write"],
    updated: "2026-05-24",
    excerpt: "Most common cause: you don't have Write access for that terminal's region.",
    blocks: [
      { type: "p", text: "Edit access in TCS is scoped by region. If you hold a View role (or no role) in the terminal's region, the Edit button will not appear — or the API will reject the change." },
      { type: "ul", items: [
        "Check the region of the terminal on the rate. Is it within your assigned region(s)?",
        "Open Access Requests and submit a Write access request for the additional region if needed.",
        "If you believe you already have Write access, contact your admin to verify your role assignment in Admin Console → Users.",
      ]},
    ],
    related: ["role-matrix", "wf-access"],
  },
  {
    id: "faq-calc-zero", category: "faq", title: "Calculator shows $0 — what's wrong?",
    tags: ["calculator", "zero", "active rate", "roe", "no rate"],
    updated: "2026-05-24",
    excerpt: "Two common causes: no active rate for that combination, or missing ROE.",
    blocks: [
      { type: "ul", items: [
        "No Active rate covers the selected terminal × cost type × equipment × size combination on the chosen effective date. Open Rate Repository and check whether a rate exists and is Active.",
        "ROE is missing for the rate's currency on that date. Open ROE Table and confirm an entry exists for the currency with an effective date on or before your selected date.",
        "The rate's status is Draft or Inactive — only Active rates are used in the calculator.",
      ]},
    ],
    related: ["mod-calc", "mod-roe", "mod-health"],
  },
  {
    id: "faq-qv-errors", category: "faq", title: "My rate shows QV errors — how do I fix them?",
    tags: ["qv", "validation", "error", "warning", "quality validation", "fix"],
    updated: "2026-05-24",
    excerpt: "What each QV rule means and how to resolve it.",
    blocks: [
      { type: "p", text: "Open Rate Health → Quality Validation to see all current violations. Click the target rate ID (e.g. R-10045) to open that rate directly in Rate Repository." },
      { type: "kv", rows: [
        ["QV-01 Duplicate record", "Two rates share identical fields. Delete or deactivate the redundant one."],
        ["QV-04 Expiry before effective", "Set expiry_date to a date after effective_date."],
        ["QV-05 Zero/negative rate", "Update rate_local to a positive value. If the rate is genuinely zero, check with your rate focal."],
        ["QV-06 Slab range invalid", "Ensure slab_min ≤ slab_max. Set slab_max to NULL (not 0) for open-ended slabs. Set slab_min to 1 (not 0) for first-slab starts."],
        ["QV-07 Missing ROE", "Add an ROE entry for the currency in the ROE Table — or update the rate to USD if appropriate."],
        ["QV-08 Required field missing", "Fill in the dimension field flagged. Which fields are required is configured per cost type in Admin Console → Metadata → Cost Types."],
        ["QV-09/QV-10 Rate order", "Review the rate_local values across the flagged size / equipment group and correct the outlier."],
        ["QV-11/QV-12/QV-13 Duplicate flag", "Only one rate in the group may have this flag set. Clear the flag on the duplicate."],
      ]},
    ],
    related: ["mod-health", "mod-rates", "admin-metadata"],
  },
  {
    id: "faq-completeness", category: "faq", title: "A terminal shows Missing — what does that mean?",
    tags: ["completeness", "missing", "coverage matrix", "rate health"],
    updated: "2026-05-24",
    excerpt: "Missing means the Coverage Matrix expects a rate for that combination but none is found.",
    blocks: [
      { type: "p", text: "A Missing indicator in Rate Health → Completeness means that the Coverage Matrix has a required entry for a terminal × cost type × dimension combination, but no matching Active rate exists." },
      { type: "h2", text: "Steps to resolve" },
      { type: "ol", items: [
        "Click the terminal card or the Missing row to see exactly which cost type / equipment combination is missing.",
        "Open Rate Repository → Add Rate and create the missing rate.",
        "Alternatively, if the combination is no longer required, remove or deactivate that entry in Admin Console → Coverage Matrix.",
      ]},
    ],
    related: ["mod-health", "admin-coverage", "mod-rates"],
  },

  // -------- Release Notes --------
  {
    id: "rel-1-1-0", category: "releases", title: "v1.1.0 — TCS full launch",
    tags: ["release", "v1.1", "tcs", "rate health", "roe", "calculator", "access", "admin"],
    updated: "2026-05-24", author: "Ocean Procurement",
    version: "1.1.0", releaseDate: "2026-05-24",
    excerpt: "Full launch — system rebranded to TCS, all modules live.",
    blocks: [
      { type: "callout", tone: "tip", title: "What's new in v1.1",
        text: "System rebranded from TCMS to TCS (Terminal Cost Simulation System) as part of the APA Ocean Procurement standardization initiative. All core modules are now live." },
      { type: "h2", text: "Modules released" },
      { type: "kv", rows: [
        ["Rate Repository", "Full rate CRUD, batch edit, Excel template import, Rateflow raw data import, export."],
        ["Rate Health", "Completeness matrix (Coverage Matrix driven), Expiry Alerts with successor detection, Quality Validation with 13 QV rules."],
        ["ROE Table", "Currency exchange rate management with effective/expiry date support."],
        ["Rate Calculator", "USD cost simulation with slab/tiered rate support and active ROE lookup."],
        ["Access Requests", "Self-service access request portal with region-based approval routing."],
        ["Admin Console", "Users, Permissions, Metadata (9 entity types), Coverage Matrix, Approval Routing."],
      ]},
      { type: "h2", text: "Quality Validation rules (QV)" },
      { type: "ul", items: [
        "QV-01 Duplicate record (Error)",
        "QV-04 Expiry before effective (Error)",
        "QV-05 Zero / negative rate (Warning)",
        "QV-06 Slab range invalid (Error / Warning)",
        "QV-07 Missing ROE (Warning)",
        "QV-08 Required field missing (Configurable per cost type)",
        "QV-09 Container size rate order — 45 ≥ 40 ≥ 20 (Warning)",
        "QV-10 Container type rate order — Dry must be lowest (Warning)",
        "QV-11 Duplicate STCY flag (Error)",
        "QV-12 Duplicate invoice rate flag (Error)",
        "QV-13 Duplicate calculator flag (Error)",
      ]},
      { type: "h2", text: "Data source" },
      { type: "ul", items: [
        "Rate data sourced from CYB and CYM Rateflow exports.",
        "Rateflow v2 and v3 export formats supported in the Import wizard.",
        "Coverage Matrix auto-populate generates expected coverage from existing Active rates.",
      ]},
    ],
    related: ["mod-rates", "mod-health", "mod-calc", "mod-roe"],
  },
  {
    id: "rel-1-0-0", category: "releases", title: "v1.0.0 — Initial launch (Rate Repository)",
    tags: ["release", "v1.0", "launch", "mvp", "rate repository"],
    updated: "2026-04-15",
    version: "1.0.0", releaseDate: "2026-04-15",
    excerpt: "MVP launch — core rate management and access control.",
    blocks: [
      { type: "h2", text: "MVP scope" },
      { type: "ul", items: [
        "Core rate CRUD with online form and Excel template import.",
        "Rate status management (Active, Draft, Inactive, Schedule).",
        "Role-based access control with regional scoping (View, Write, Admin).",
        "Access Request workflow with approval routing.",
        "Admin Console: user management, metadata, ROE table, audit log.",
        "Export to Excel with filter support.",
      ]},
      { type: "callout", tone: "info", title: "Rollout",
        text: "Initial rollout to APA region covering North Asia, SE Asia, South Asia, and Oceania." },
    ],
    related: ["gs-overview"],
  },
];

// Flat keyword index for search
const DOC_INDEX = DOC_ARTICLES.map(a => ({
  ...a,
  searchText: [
    a.title,
    a.excerpt,
    ...(a.tags || []),
    ...(a.blocks || []).flatMap(b => {
      if (b.type === "p" || b.type === "h2" || b.type === "h3" || b.type === "code") return [b.text];
      if (b.type === "ul" || b.type === "ol") return b.items || [];
      if (b.type === "kv") return (b.rows || []).flat();
      if (b.type === "table") return [(b.headers || []).join(" "), ...(b.rows || []).flat()];
      if (b.type === "callout") return [b.title, b.text];
      return [];
    }),
  ].join(" \n ").toLowerCase(),
}));

Object.assign(window, { DOC_CATEGORIES, DOC_ARTICLES, DOC_INDEX });
