{
  "aliases": [
    "error-surface",
    "errorsurface",
    "403",
    "404",
    "500",
    "503",
    "exception-page"
  ],
  "code": "// @godxjp/ui SHIPS <ErrorSurface>. Do NOT hand-compose an error page from\n// the auth CenteredShell + a generic Card + a local \".canonical-auth-card\" — that workaround IS the regression.\n//\n//    403 / 404  → mode=\"application\": the surface is the BODY of the AppShell you ALREADY render\n//                 (sidebar + topbar + breadcrumb are PRESERVED, never reconstructed)\n//    500 / 503  → mode=\"system\":      the surface OWNS the page (CenteredShell align=\"center\",\n//                 package-owned geometry at 1440 / 1024 / 390 — no min-h-dvh, no media query)\n// Docs page: docs/layout/error-surface/\n\nimport { Button, Logo, Text } from \"@godxjp/ui/general\";\nimport { AppShell, ErrorSurface, PageContainer, Sidebar } from \"@godxjp/ui/layout\";\n\n// ── WHAT THE COMPONENT OWNS ───────────────────────────────────────────────────────────\n//  • status → icon + tone (ShieldAlert/warning · SearchX/muted · ServerCrash/destructive ·\n//    Wrench/warning). You pass the NUMBER; you never write the lookup.\n//  • EXACTLY ONE recovery action — a single slot; a 2nd element is dropped with a dev error.\n//  • semantic metadata slots as real <dt>/<dd> pairs, with LIBRARY-owned localized labels:\n//    requestId · permission · organization · maintenance{start,end,timeZone,progress}\n//  • heading order (h2 in application mode under the PageContainer h1, h1 in system mode)\n//  • the status code announced as \"HTTP status 403\", not the cardinal number \"403\"\n// PRODUCT COPY stays yours: title / description / action come from the APP's own t().\n\n// ── 403 / 404 — APPLICATION mode: PRESERVE the authenticated shell ─────────────────────\nexport function ForbiddenPage() {\n  return (\n    <AppShell sidebar={<Sidebar activeId=\"reports\" sections={sections} />}>\n      <PageContainer title={t(\"reports.title\")} breadcrumb={[{ label: t(\"nav.home\"), to: \"/\" }, { label: t(\"reports.title\") }]}>\n        <ErrorSurface\n          mode=\"application\"\n          status={403}\n          title={t(\"errors.403.title\")}\n          description={t(\"errors.403.description\")}\n          permission=\"reports.view\"        {/* WHY: the missing role … */}\n          organization=\"Acme KK\"           {/* … vs the wrong workspace */}\n          action={<Button onClick={goHome}>{t(\"errors.backHome\")}</Button>}\n        />\n      </PageContainer>\n    </AppShell>\n  );\n}\n\n// ── 500 / 503 — SYSTEM mode: package-owned viewport-centred geometry ───────────────────\n// The component renders CenteredShell align=\"center\" itself → the column centres in the 100dvh\n// shell at 1440 / 1024 / 390. NO className, NO min-h-dvh, NO media query. Tall copy still scrolls\n// from the top (auto offsets collapse), so a long JA/VI message is never clipped.\nexport function MaintenancePage() {\n  return (\n    <ErrorSurface\n      mode=\"system\"\n      status={503}\n      brand={<Logo glyph=\"G\" />}\n      title={t(\"errors.503.title\")}\n      description={t(\"errors.503.description\")}\n      maintenance={{\n        start: \"2026-08-02T18:00:00Z\",  // ISO-8601 instants + an IANA zone → Intl.formatRange\n        end: \"2026-08-02T20:00:00Z\",\n        timeZone: \"Asia/Tokyo\",\n        progress: 40,                   // 0-100, SERVER-SENT (a client clock breaks hydration)\n      }}\n      footer={<Text size=\"xs\" tone=\"muted\">2026 GodX</Text>}\n      action={<Button onClick={reload}>{t(\"errors.reload\")}</Button>}\n    />\n  );\n}\n\n// ── INERTIA / SSR — one Error page component, one expression ───────────────────────────\n// Laravel: Handler::render() → Inertia::render('Error', ['status' => $response->getStatusCode()])\n//\n//   export default function Error({ status, requestId }) {\n//     const isSystem = status >= 500\n//     return (\n//       <ErrorSurface\n//         mode={isSystem ? \"system\" : \"application\"}\n//         status={status}\n//         requestId={requestId}\n//         title={t(`errors.${status}.title`)}\n//         description={t(`errors.${status}.description`)}\n//         action={<Button asChild><Link href=\"/\">{t(\"errors.backHome\")}</Link></Button>}\n//       />\n//     )\n//   }\n//   // persistent layout ⇒ 403/404 keep the authenticated shell; 500/503 own the page\n//   Error.layout = (page) => (page.props.status < 500 ? <CrmLayout>{page}</CrmLayout> : page)\n//\n// SSR rules:\n//  • ErrorSurface has NO client state / effects / portals → it renders fully server-side. An\n//    exception page must be readable without hydration.\n//  • Pass the maintenance window as a SERVER-SENT ISO-8601 string + an explicit IANA timeZone.\n//    An argless new Date() or the server's local zone ⇒ hydration mismatch.\n//  • A 500 page must NOT import the app's query client / data providers — the failure may be\n//    inside them. ErrorSurface needs only the AppProvider that supplies the locale.\n//\n// THEMING: retune with tokens, never a className —\n//   --error-surface-max-width | -gap | -padding-block(-compact) | -meta-gap | -meta-row-gap |\n//   --error-surface-meta-border (default 'none', rule #44) | --error-surface-progress-max-width\n//\n// ANTI-PATTERNS: the auth CenteredShell + a generic Card (the workaround; it is the\n// UNAUTHENTICATED root with auth-card geometry) · rebuilding nav on the 403 page · two CTAs ·\n// className=\"min-h-dvh flex …\" · a hand-built \"18:00 - 20:00 JST\" string · writing the request id\n// into 'description' as prose instead of using the semantic slot.",
  "name": "error-pages",
  "tagline": "400 / 403 / 404 / 500 / 503 exception pages. USE THE COMPONENT: import { ErrorSurface } from \"@godxjp/ui/layout\". `mode=\"application\"` (400/403/404) is the body you put inside the AppShell the route already renders; `mode=\"system\"` (500/503) owns the page via CenteredShell align=\"center\".",
  "tags": [
    "error",
    "errorsurface",
    "403",
    "404",
    "500",
    "503",
    "maintenance",
    "inertia",
    "ssr",
    "layout",
    "emptystate",
    "centeredshell"
  ]
}
