// EarlyAccessGate.jsx
//
// The "Early Access Demo" state every unbuilt route resolves to - product/
// solution detail pages not yet written, and the permanently lead-capture-
// only Resources/Developers/Case-Studies pages (SPEC-07's anti-hallucination
// conversion pattern) - instead of a 404 or invented content.
//
// REAL_MODULES is the same 12-module, code-grounded list (SPEC-07 Section 0)
// that index.html's ModuleGrid renders on the Home page; it lives here,
// rather than in index.html, so this file's title lookup doesn't depend on
// load order. index.html references the global `REAL_MODULES` this file
// declares (not a redeclaration - just a read), since it loads after this
// script.
//
// Loaded as a classic (non-module) Babel-transformed script - see
// index.html's header comment. Uses no hooks, so it doesn't depend on
// `window.React` being ready at load time (only JSX, resolved at render
// time via Babel's classic `React.createElement` transform).

// 15 modules - each a real, distinct module/entity in trans-backend
// (SPEC-07 Section 0), not a marketing round-number. Where earlier drafts
// grouped related modules into one card ("Partner Management", "Inventory
// & Multi-Warehouse"), they're listed separately here since each is in
// fact its own backend module (CustomerModule/VendorModule/BrokerModule,
// InventoryModule/WarehouseModule) - this is a more granular, equally
// honest count, not an inflated one.
const REAL_MODULES = [
  {
    id: "fleet-dispatch",
    name: "Fleet & Dispatch",
    blurb: "Track every shipment from pending to delivered or disputed, linked directly to the sales orders it fulfills.",
    tagline: "From confirmed order to proof of delivery, without a phone call to check status.",
    capabilities: [
      "Shipment lifecycle: Pending → Dispatched → Delivered or Disputed",
      "Linked directly to the sales orders it fulfills through a real relationship, not a manual note",
      "Customers confirm or dispute their own delivery through the Customer Portal - no login required",
      "Multi-item shipments tracked line by line, not as one bulk entry",
    ],
    painPoint:
      "Dispatch status usually lives in someone's head, or in a phone call to the driver. Sales tells a customer \"it's on the way\" without knowing if it actually left, and a dispute becomes a word-for-word argument with no record either side can point to.",
    howItWorksDetail:
      "Every shipment carries a real status - Pending, Dispatched, Delivered, or Disputed - tied directly to the sales order it fulfills, not typed into a free-text note. Once dispatched, the customer gets a link to the Customer Portal where they confirm or dispute the delivery themselves, with a description if something's wrong. That record is locked the moment the shipment reaches a final state, so it can't quietly be edited after the fact by either side.",
    useCases: [
      "A customer calls asking where their order is - you check one screen instead of calling the driver.",
      "A delivery is disputed - you have a timestamped customer-submitted record, not a phone call you half-remember.",
      "A shipment covers five different products - each is tracked as its own line, not folded into one dispatch note.",
    ],
    faqs: [
      {
        q: "Does the customer need to install anything or create an account?",
        a: "No. They get a link to the Customer Portal for that specific shipment - no login, no app.",
      },
      {
        q: "Can a shipment cover multiple sales orders?",
        a: "Yes - shipments link to sales orders through their own relationship, and a dispatch can cover more than one order.",
      },
      {
        q: "What happens after a delivery is confirmed or disputed?",
        a: "The record locks once the shipment reaches that final state, so it stays a reliable reference instead of something that can quietly change later.",
      },
    ],
  },
  {
    id: "sales-orders",
    name: "Sales Orders",
    blurb: "Confirmed → partially shipped → shipped, driven by real stock - not a spreadsheet copy of it.",
    tagline: "Orders move through a real lifecycle, driven by what's actually in stock.",
    capabilities: [
      "Status lifecycle: Confirmed → Partially Shipped → Shipped",
      "Tied to live inventory movement, not a manually updated status flag",
      "Partial shipments are a first-class state, not a workaround",
      "Feeds Fleet & Dispatch and Analytics without re-entering data",
    ],
    painPoint:
      "A sales order sitting in a spreadsheet doesn't know whether the goods behind it have actually moved. Someone has to remember to update the status by hand - and \"partially shipped\" usually gets rounded off to either fully open or fully closed, because a spreadsheet has no real in-between state.",
    howItWorksDetail:
      "A Sales Order's status is driven by the same stock-movement events that update Inventory, not a dropdown someone forgets to change. When part of an order ships, the order itself moves to Partially Shipped - a real, distinct state, not a workaround bolted onto \"Open\" or \"Closed.\" That status feeds Fleet & Dispatch and every dashboard that reports on sales without anyone re-entering the same information twice.",
    useCases: [
      "You ship half an order today and the rest next week - the order reflects that accurately the whole time, not just at the end.",
      "A customer asks for order status - the answer comes from the same stock events driving dispatch, not a status someone forgot to update.",
      "Finance wants to know what's confirmed-but-not-yet-shipped - that's a real, queryable state, not a manual filter on a spreadsheet.",
    ],
    faqs: [
      {
        q: "Who updates the order status when a shipment goes out?",
        a: "Nobody has to - the status moves automatically based on the same stock-movement event that drives Fleet & Dispatch.",
      },
      {
        q: "Is a partially shipped order treated as open or closed?",
        a: "Neither - it's its own real status, so reporting on it doesn't require guessing which bucket it belongs in.",
      },
      {
        q: "Does this module require Inventory to be set up first?",
        a: "Yes - Sales Orders' status changes are driven by real stock movement, so Inventory Management is the dependency underneath it.",
      },
    ],
  },
  {
    id: "purchase-grn",
    name: "Purchase Orders & GRN",
    blurb: "Purchase orders reconcile against goods-received notes line by line, from pending to fully received.",
    tagline: "Every purchase order reconciles against what was actually received, line by line.",
    capabilities: [
      "Status lifecycle: Pending → Partially Received → Completed",
      "One GRN row per product per receipt - partial deliveries tracked exactly, not rounded off",
      "Received stock flows straight into Inventory, and into Production where applicable",
      "Cancellation is a tracked status, not a deleted record",
    ],
    painPoint:
      "A vendor delivers 80% of an order and promises the rest \"next week\" - and on paper, that purchase order either gets marked received (wrong) or left open indefinitely (also wrong), because most systems don't have a real partial-receipt state.",
    howItWorksDetail:
      "Goods-Received Notes track exactly what arrived, one row per product per receipt - so a partial delivery is recorded as exactly that, not rounded up or left ambiguous. The purchase order's own status - Pending, Partially Received, Completed - reflects the real total across every GRN against it. Received stock flows straight into Inventory (and into Production, for raw materials feeding a workflow) the moment it's logged, not on a batch import at month-end.",
    useCases: [
      "A vendor short-ships an order - the GRN records exactly what arrived, and the PO stays accurately Partially Received until the rest comes in.",
      "Finance needs to know what's been paid for versus what's actually landed - GRN records are the real source, not a purchase order assumed to be complete.",
      "Raw material for production arrives - it's available in Inventory and to the Production workflow the moment it's received, not after a nightly sync.",
    ],
    faqs: [
      {
        q: "What happens if a vendor delivers more than one shipment against the same PO?",
        a: "Each delivery gets its own GRN, and the purchase order's status reflects the running total across all of them.",
      },
      {
        q: "Can a purchase order be cancelled after it's created?",
        a: "Yes - cancellation is a real, tracked status, not a deleted record, so there's still a history of what was ordered and why it didn't proceed.",
      },
      {
        q: "Does received stock show up in Inventory immediately?",
        a: "Yes - a GRN is itself a stock-in event, so Inventory (and Production, where relevant) reflects it right away.",
      },
    ],
  },
  {
    id: "inventory",
    name: "Inventory Management",
    blurb: "Live per-product stock balances that update the moment a purchase, sale, or production event moves goods.",
    tagline: "Stock balances that update the moment goods actually move.",
    capabilities: [
      "Per-product, per-warehouse balances kept live by the same events that drive the Production WIP ledger",
      "No manual stock-take spreadsheet sitting between the warehouse and the system",
      "Feeds Analytics & Dashboards directly - no separate export step",
    ],
    painPoint:
      "The number on the spreadsheet and the number on the shelf drift apart the moment anyone forgets to update one of them - and by the time a monthly stock-take catches the gap, it's already caused a wrong promise to a customer or an unnecessary purchase order.",
    howItWorksDetail:
      "Inventory balances are a live consequence of the same events that drive Purchase Orders, Sales Orders, and the Production workflow - a stock-in, stock-out, or GRN receipt updates the number immediately, not on a scheduled sync. Because every other module reads from this same balance instead of keeping its own copy, there's exactly one number to trust, per product, per warehouse.",
    useCases: [
      "Sales wants to know what's actually available before promising a delivery date - the number is live, not from last week's count.",
      "A discrepancy shows up in a physical count - you can trace it against the real event history instead of guessing when it happened.",
      "Finance needs a stock valuation snapshot - it comes from the same live balances every other module already trusts.",
    ],
    faqs: [
      {
        q: "Do I still need to do periodic physical stock counts?",
        a: "Physical counts are still good practice for catching real-world discrepancies (damage, shrinkage) - the difference is you're reconciling against a live system number, not a stale one.",
      },
      {
        q: "Does Inventory work the same with one warehouse or several?",
        a: "Yes - balances are tracked per warehouse under the hood from day one, whether you're running one location or several through Multi-Warehouse Management.",
      },
      {
        q: "What actually moves the inventory number?",
        a: "Purchase receipts (GRN), sales dispatches, and production stage transitions - the same events that drive every other module, not a separate manual adjustment screen.",
      },
    ],
  },
  {
    id: "warehouses",
    name: "Multi-Warehouse Management",
    blurb: "Run multiple warehouses per company with a default-warehouse fallback when a movement doesn't name one.",
    tagline: "Run one warehouse or ten, with a default-warehouse fallback built in.",
    capabilities: [
      "Company-scoped warehouse records, each toggled default or not",
      "A stock movement with no warehouse specified resolves to the default automatically, instead of failing",
      "The same inventory and dashboard views work whether you run 1 warehouse or many",
    ],
    painPoint:
      "Adding a second location usually means retrofitting a system that only ever expected one - stock gets tracked as one combined number, and nobody can say with confidence what's actually sitting at the depot versus the main site.",
    howItWorksDetail:
      "Warehouses are a real, company-scoped entity from the start, not a location field bolted onto Inventory later - each one can be toggled as the default. Any stock movement that doesn't explicitly name a warehouse resolves to that default automatically instead of failing or landing in the wrong place, which matters most for integrations and quick-entry flows where specifying a warehouse every time isn't realistic.",
    useCases: [
      "You open a second depot - it's a new warehouse record, not a system migration.",
      "A quick stock entry doesn't specify a location - it lands in the default warehouse automatically instead of erroring out.",
      "You want to know what's at the depot versus the main site - that's a real, separate balance per warehouse, not a combined guess.",
    ],
    faqs: [
      {
        q: "Is there a limit to how many warehouses a company can have?",
        a: "No - warehouses are a standard company-scoped record, so the model doesn't assume a fixed number.",
      },
      {
        q: "What happens if I don't set a default warehouse?",
        a: "Each company can mark one warehouse as default; movements that don't specify a location rely on that setting, so it's worth setting early.",
      },
      {
        q: "Do dashboards break down by warehouse automatically?",
        a: "Yes - the Warehouse Analytics dashboard and Inventory balances are both tracked per warehouse, not just at the company level.",
      },
    ],
  },
  {
    id: "production-workflow",
    name: "Production & WIP Workflow Engine",
    blurb: "Configurable multi-stage recipes with a double-entry WIP ledger tracking in-progress stock and wastage.",
    tagline: "A configurable, multi-stage production engine - not a single 'in production' checkbox.",
    capabilities: [
      "Per-company recipes and stages you define, not a hardcoded process",
      "A double-entry WIP ledger tracks exactly what's sitting at each stage",
      "Automatic wastage and yield-loss evaluation as stock moves between stages",
      "Reacts to the same stock-in/stock-out/GRN events that drive Inventory - one event stream, not a parallel system",
    ],
    painPoint:
      "Most systems treat production as a single black box: raw material goes in, finished goods come out, and everything in between is invisible until a month-end physical count tells you how much didn't make it - with no way to say which stage lost it.",
    howItWorksDetail:
      "You define your own stages and recipes - this engine doesn't assume a fixed process. A double-entry WIP ledger tracks exactly what's sitting at each stage in real time, and a wastage evaluation runs per stage, per cycle, flagging yield loss and negative balances automatically as material moves through. It reacts to the same stock-in, stock-out, and GRN events that drive Inventory - so production visibility isn't a separate system that needs reconciling against the real one.",
    useCases: [
      "Raw material moves from extrusion into blending - the WIP ledger reflects that transition immediately, per stage.",
      "Yield loss spikes at one specific stage - you see it flagged there, not as one blended number for the whole plant at month-end.",
      "Two products co-produce from the same input - the ledger tracks each by product, not one merged balance you can't separate.",
    ],
    faqs: [
      {
        q: "Do I have to use a fixed, predefined production process?",
        a: "No - stages and recipes are configured per company, so they match how you actually run production, not a generic template.",
      },
      {
        q: "How is wastage actually calculated?",
        a: "Per stage, per cycle: accumulated stock in, minus accumulated stock out, minus what's still legitimately in progress - the difference is evaluated as wastage automatically.",
      },
      {
        q: "What if a stage's balance goes negative?",
        a: "It's flagged as a diagnostic warning rather than silently blocked or clamped to zero, since the physical stock movement that caused it already happened and can't be un-done retroactively.",
      },
    ],
  },
  {
    id: "business-rules",
    name: "Business Rules Engine",
    blurb: "Per-unit, per-weight, percentage, and threshold strategies - with an immutable snapshot on every applied record.",
    tagline: "Commission and pricing rules that calculate themselves - and never silently rewrite history.",
    capabilities: [
      "Five calculation strategies: per-unit, per-weight, percentage, purchase-threshold, sales-threshold",
      "Rules assign to a specific broker, customer, or vendor - not one global setting for everyone",
      "Every applied rule is snapshotted immutably, so editing a rule later never changes a past calculation",
    ],
    painPoint:
      "Commission and pricing rules usually live in someone's memory, or in a spreadsheet formula that quietly breaks the moment a rate changes - and if last quarter's rule gets edited today, last quarter's numbers can silently change with it.",
    howItWorksDetail:
      "Rules are built from five real calculation strategies - per-unit, per-weight, percentage, purchase-threshold, and sales-threshold - and assigned to a specific broker, customer, or vendor rather than applied as one blanket setting. The moment a rule fires on a real record, an immutable snapshot of that calculation is stored with it, so editing the rule afterward changes future calculations only, never the historical ones already on the books.",
    useCases: [
      "A broker earns a different commission rate above a certain volume - that's a sales-threshold strategy, not a manual spreadsheet lookup.",
      "You renegotiate a vendor's pricing mid-year - past purchase orders keep their original snapshot; only new ones use the updated rule.",
      "Two brokers need completely different commission structures - each gets their own rule assignment, not one shared setting.",
    ],
    faqs: [
      {
        q: "What happens to past calculations if I edit a rule?",
        a: "Nothing - each applied rule stores its own immutable snapshot at the moment it fired, so editing the rule only affects records calculated after the change.",
      },
      {
        q: "Can different customers or brokers have completely different rules?",
        a: "Yes - rules are assigned per entity, not applied globally, so pricing and commission structures can differ across your whole partner base.",
      },
      {
        q: "What if none of the five strategies fit what I need?",
        a: "Tell us on a demo call - if it's a genuinely common need, it's the kind of thing we build (see our onboarding process on the Home page).",
      },
    ],
  },
  {
    id: "analytics-dashboards",
    name: "Analytics & Dashboards",
    blurb: "Registry-driven drill-down dashboards across purchase, sales, stock, warehouse, and production pipeline data.",
    tagline: "Drill-down dashboards built from the same data your team already enters.",
    capabilities: [
      "Dashboards across purchase, sales, stock, warehouse, and production-pipeline data",
      "Level-two drill-downs go from a summary card to the underlying records in one click",
      "Permission-aware - a dashboard only shows what that user's role is allowed to see",
    ],
    painPoint:
      "Reporting usually means exporting data out of the system that generated it into a spreadsheet or BI tool someone maintains separately - by the time the report is built, it's already describing last week.",
    howItWorksDetail:
      "Dashboards are registry-driven, reading directly from the same records every other module writes - there's no separate export or nightly sync feeding a BI tool. A summary card's number is one click away from the underlying records that produced it, and every dashboard respects the viewer's own role permissions, so what a Staff user sees is automatically scoped to what they're allowed to touch.",
    useCases: [
      "You need this week's purchase spend by vendor - it's a live dashboard, not a spreadsheet someone rebuilds every Monday.",
      "A summary number looks off - you drill down one click to the actual records behind it instead of re-running an export.",
      "A Staff-level user opens the dashboard - they see only the modules their role permits, without a separate access-control system to maintain.",
    ],
    faqs: [
      {
        q: "Do I need a separate BI tool to get useful reports?",
        a: "No - dashboards are built into the platform and read the same live data every other module uses, so there's no export/import cycle to maintain.",
      },
      {
        q: "Can I filter dashboards by date range, vendor, or product?",
        a: "Yes - dashboards support contextual filters (date range, and entity filters like vendor, broker, customer, or product depending on the view).",
      },
      {
        q: "Will every user see the same dashboards?",
        a: "No - each dashboard is permission-aware, so what's visible depends on the viewer's own role configuration.",
      },
    ],
  },
  {
    id: "ai-assistant",
    name: "AI Business Assistant",
    blurb: "A multi-language assistant that answers questions across your own live orders, shipments, and partners.",
    tagline: "Ask about your own orders and shipments in plain language - and get an answer, not a query builder.",
    capabilities: [
      "Understands business terminology in multiple languages, starting with Gujarati and English",
      "Scoped strictly to your own company's data - never another tenant's",
      "Answers by querying your live orders, shipments, vendors, and brokers, not a static FAQ",
    ],
    painPoint:
      "Getting a specific answer out of most systems means knowing which report to run, which filters to apply, and which screen to check - so the person who actually needs the answer usually just asks someone else to look it up for them.",
    howItWorksDetail:
      "The assistant understands business terminology - including bilingual Gujarati/English terms - and answers by directly querying your own live orders, shipments, vendors, and brokers, not a pre-written FAQ. Every query is scoped strictly to your company's own data, the same tenant isolation every other module enforces, so it can never see or reference another company's records.",
    useCases: [
      "You want to know which vendors haven't delivered this month, in Gujarati - you ask directly instead of building a filtered report.",
      "A manager without dashboard access still needs a quick number - the assistant answers within their own permission scope.",
      "You're checking something across shipments and purchase orders at once - one question instead of two separate screens.",
    ],
    faqs: [
      {
        q: "Can the assistant see or answer using another company's data?",
        a: "No - it's scoped to the caller's own company, the same tenant isolation enforced everywhere else in the platform.",
      },
      {
        q: "What languages does it actually understand?",
        a: "It's built with a bilingual Gujarati/English business-term dictionary, matching the platform's own localization approach.",
      },
      {
        q: "Can it make changes to my data, or only answer questions?",
        a: "It answers by querying your existing records - it's a lookup and insight tool, not a mechanism for editing your data.",
      },
    ],
  },
  {
    id: "customer-portal",
    name: "Customer Portal",
    blurb: "A public, no-login link your customers use to confirm or dispute a delivery themselves.",
    tagline: "Your customers confirm or dispute their own delivery, without creating an account.",
    capabilities: [
      "One shareable link per shipment, no login required",
      "Customers confirm receipt or raise a dispute with a description directly",
      "Locked automatically once a shipment reaches a final state, preventing after-the-fact edits",
    ],
    painPoint:
      "Confirming a delivery usually means a phone call or a WhatsApp message that never makes it into any system - so there's no record when a dispute comes up weeks later, just two different memories of what was said.",
    howItWorksDetail:
      "Each shipment gets its own shareable link - no account, no app, no login required - where the customer confirms receipt or raises a dispute with a written description. That record locks automatically the moment the shipment reaches a final state, so it becomes a durable reference both sides can point back to instead of a conversation nobody wrote down.",
    useCases: [
      "A delivery arrives - the customer confirms it themselves in a few taps, no account needed.",
      "Something's wrong with an order - the customer describes it directly, creating a real record instead of an unlogged phone call.",
      "A dispute comes up a month later - you have a timestamped, locked record instead of relying on memory.",
    ],
    faqs: [
      {
        q: "Does the customer need an account or app?",
        a: "No - it's a public link scoped to that one shipment, with no login required.",
      },
      {
        q: "Can a customer edit their confirmation after submitting it?",
        a: "No - the record locks once the shipment reaches a final state, which is what makes it a reliable reference later.",
      },
      {
        q: "Is this connected to Fleet & Dispatch?",
        a: "Yes - the Customer Portal is the customer-facing side of the same shipment record Fleet & Dispatch manages internally.",
      },
    ],
  },
  {
    id: "customers",
    name: "Customer Management",
    blurb: "A single customer record feeding sales, shipments, and every dashboard that segments by customer.",
    tagline: "One customer record, used everywhere that customer shows up.",
    capabilities: [
      "Feeds Sales Orders, Fleet & Dispatch, and every dashboard that segments by customer",
      "The same record customers see themselves through the Customer Portal",
    ],
    painPoint:
      "The same customer often ends up typed into three different places - a sales spreadsheet, a dispatch register, and someone's contact list - so their details drift out of sync and nobody's sure which version is current.",
    howItWorksDetail:
      "A customer is one record, referenced by Sales Orders, Fleet & Dispatch, and every dashboard that segments by customer - not re-entered in each module separately. It's the same record the customer interacts with indirectly through the Customer Portal, so there's one source of truth instead of three copies drifting apart.",
    useCases: [
      "A customer's contact details change - update it once, and every module referencing them reflects it.",
      "You want total sales by customer - the dashboard reads the same record every order already points to.",
      "A new sales rep needs customer history - it's attached to the one record, not scattered across separate files.",
    ],
    faqs: [
      {
        q: "Do I need to re-enter a customer for each sales order?",
        a: "No - you select the existing customer record, which is exactly what keeps their history connected across every order.",
      },
      {
        q: "Is customer data shared across companies on the platform?",
        a: "No - customer records are scoped to your own company like every other tenant-owned record.",
      },
    ],
  },
  {
    id: "vendors",
    name: "Vendor Management",
    blurb: "Vendor records tied directly into purchase orders and the business rules that price them.",
    tagline: "Vendor records tied directly into the purchase orders and pricing rules that use them.",
    capabilities: [
      "Feeds Purchase Orders & GRN directly",
      "Vendor-specific Business Rules apply automatically, not via a manual lookup",
    ],
    painPoint:
      "Vendor pricing agreements usually live in an email thread or a person's memory - so applying the right rate to a new purchase order means someone manually checking (or forgetting to check) before it goes out.",
    howItWorksDetail:
      "A vendor is a real record feeding Purchase Orders & GRN directly, and any Business Rule assigned to that vendor - a negotiated rate, a threshold-based term - applies automatically to purchase orders raised against them, not via a manual lookup someone has to remember to do.",
    useCases: [
      "You raise a purchase order against a vendor with a negotiated rate - the pricing rule applies automatically, not from memory.",
      "A vendor's terms change mid-year - update the rule once; past purchase orders keep their original snapshot.",
      "You need vendor-wise purchase history - it's attached to the one vendor record, not reconstructed from email threads.",
    ],
    faqs: [
      {
        q: "How does vendor-specific pricing actually apply?",
        a: "Through the Business Rules Engine - a rule gets assigned to a specific vendor, and it applies automatically on purchase orders raised against them.",
      },
      {
        q: "Can one vendor supply multiple product types?",
        a: "Yes - vendor records aren't limited to a single product relationship.",
      },
    ],
  },
  {
    id: "brokers",
    name: "Broker Management",
    blurb: "Broker-assigned commission rules calculated automatically by the same Business Rules Engine.",
    tagline: "Broker commissions calculated automatically, not tracked in a side spreadsheet.",
    capabilities: [
      "Commission rules assigned per broker - per-unit, per-weight, percentage, or threshold-based",
      "Calculated by the same Business Rules Engine every other pricing rule uses",
    ],
    painPoint:
      "Broker commissions are usually reconciled by hand at the end of the month, cross-referencing a spreadsheet against whatever deals actually closed - a process that's slow, error-prone, and hard to audit if a broker questions the number.",
    howItWorksDetail:
      "Each broker gets a commission rule assigned through the same Business Rules Engine every other pricing calculation uses - per-unit, per-weight, percentage, or threshold-based. It calculates automatically as deals close, with an immutable snapshot stored on each one, so a commission dispute has a real calculated record behind it instead of a spreadsheet reconstruction.",
    useCases: [
      "A broker's commission scales up past a volume threshold - that's a real rule, calculated automatically, not a manual month-end check.",
      "A broker questions a commission payout - you have the immutable snapshot from exactly when it was calculated.",
      "Different brokers have completely different commission structures - each gets their own rule assignment.",
    ],
    faqs: [
      {
        q: "Can two brokers have different commission structures?",
        a: "Yes - commission rules are assigned per broker individually, using any of the five Business Rules strategies.",
      },
      {
        q: "Is commission recalculated if I edit the rule later?",
        a: "No - past calculations keep their immutable snapshot from when the rule actually fired; only future calculations use the updated rule.",
      },
    ],
  },
  {
    id: "roles-permissions",
    name: "Roles & Permissions",
    blurb: "Per-company role templates with per-module View, Contribute, or Manage access - not a single fixed admin/user split.",
    tagline: "Per-company role templates - not a single fixed Admin/User split.",
    capabilities: [
      "Per-module access levels - View, Contribute, or Manage - set independently for fleet, sales, purchase, inventory, production, and more",
      "An Admin role always has full access; every other role is defined per company",
      "Enforced at the API level, not just hidden in the UI",
    ],
    painPoint:
      "Most systems offer exactly two access levels - full admin or basic user - which means either everyone can see and change everything, or the one person with full access becomes a bottleneck for every request.",
    howItWorksDetail:
      "Roles are defined per company as a real template, with independent View, Contribute, or Manage access set separately for each module - fleet, sales, purchase, inventory, production, and more. A staff member can have full access to dispatch and none to purchasing, for example. This isn't just hidden in the interface - it's enforced at the API level, so a permission gap can't be worked around by someone who knows the right URL.",
    useCases: [
      "A warehouse staff member needs to manage dispatch but shouldn't touch pricing rules - that's one role template, configured once.",
      "You bring on a part-time bookkeeper - give them View-only access to purchase and inventory, nothing else.",
      "An auditor needs read access without edit rights anywhere - Contribute and Manage stay off, View stays on.",
    ],
    faqs: [
      {
        q: "Is permission enforcement just a UI thing, or does it actually block access?",
        a: "It's enforced at the API level - a hidden button isn't the security boundary, the server-side check is.",
      },
      {
        q: "Does every company get the same default roles?",
        a: "Role templates are defined per company, so you configure them to match how your team is actually structured.",
      },
      {
        q: "What can an Admin role see?",
        a: "Admin bypasses per-module restrictions and has full access - it's the one role that isn't scoped down by the per-company template.",
      },
    ],
  },
  {
    id: "localization",
    name: "Localization & Multi-Language",
    blurb: "Country, currency, timezone, and a fully localizable interface - built to add new languages without new code.",
    tagline: "Country, currency, timezone, and a fully localizable interface - built to grow with you.",
    capabilities: [
      "Per-company country code, currency code, and timezone settings",
      "Interface strings are translation-file driven - Gujarati ships today, and adding a language is a translation file, not a rebuild",
      "The AI Assistant and the interface share the same localization layer",
    ],
    painPoint:
      "Most platforms are built assuming one language and one currency, so serving a bilingual team or an international customer means a workaround - a separate spreadsheet, a manually translated PDF, a second system entirely.",
    howItWorksDetail:
      "Country, currency, and timezone are real per-company settings, not a global assumption baked into the platform. The interface itself is translation-file driven rather than hardcoded in English, which is what makes Gujarati a real, live option today rather than a partial translation layered on top - and it's also what makes adding another language a translation file, not months of rebuild work.",
    useCases: [
      "Your team works in Gujarati day-to-day - the interface is genuinely bilingual, not translated only on the marketing pages.",
      "You invoice international customers - currency and timezone are real per-company settings, not a manual conversion.",
      "You need a specific language your team actually uses - tell us on a demo call; it's a translation file addition, not a platform rebuild.",
    ],
    faqs: [
      {
        q: "Which languages are actually available today?",
        a: "Gujarati and English are live today. The architecture is translation-file driven specifically so more can be added without rebuilding the platform.",
      },
      {
        q: "Does the AI Assistant work in the same languages as the interface?",
        a: "Yes - it shares the same localization layer, including the bilingual Gujarati/English business-term dictionary.",
      },
      {
        q: "Can different users on the same team use different languages?",
        a: "Language is a per-user setting layered on top of the company's own country/currency/timezone configuration.",
      },
    ],
  },
];

function humanizeSlug(slug) {
  return slug
    .split("-")
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(" ");
}

function EarlyAccessGate({ path }) {
  const segments = path.split("/").filter(Boolean);
  const lastSegment = segments[segments.length - 1] || "platform";
  const known = REAL_MODULES.find((m) => m.id === lastSegment);
  const title = known ? known.name : humanizeSlug(lastSegment);

  return (
    <section className="mx-auto flex max-w-2xl flex-col items-center px-6 py-24 text-center">
      <span className="mb-5 inline-flex items-center gap-1.5 rounded-full border border-brand-green/30 bg-emerald-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-emerald-700">
        <svg className="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
          <rect x="5" y="11" width="14" height="9" rx="2" />
          <path d="M8 11V7a4 4 0 0 1 8 0v4" />
        </svg>
        Early Access Portal
      </span>
      <h1 className="text-3xl font-bold text-brand-navy sm:text-4xl">{title}</h1>
      <p className="mt-4 text-base leading-relaxed text-slate-600">
        This page is still being written - we'd rather ship it once it's accurate than fill it with
        placeholder content. In the meantime, our team can walk you through {title.toLowerCase()} live,
        on your own data.
      </p>
      <div className="mt-8 flex flex-col gap-3 sm:flex-row">
        <a
          href="/request-demo/"
          className="rounded-card bg-emerald-600 px-6 py-3 text-sm font-semibold text-white shadow-card transition hover:bg-emerald-700"
        >
          Request a Demo
        </a>
        <a
          href="/"
          className="rounded-card border border-slate-200 bg-white px-6 py-3 text-sm font-semibold text-brand-navy shadow-card transition hover:border-brand-navy/30"
        >
          Back to Home
        </a>
      </div>
    </section>
  );
}
