> For the complete documentation index, see [llms.txt](https://docs.layeronecloud.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.layeronecloud.com/platform/design-system.md).

# Design system and front end

console and the client portal. It replaced the Console 2.x visual contract without adding a parallel skin or changing route, ownership, access-mode or partial-navigation behaviour.

## 12.1 Console 3.0 <a href="#id-121-console-30" id="id-121-console-30"></a>

**Console 3.0 is the shared authenticated control deck** for the operations console and the client portal. It replaced the Console 2.x visual contract without adding a parallel skin or changing route, ownership, access-mode or partial-navigation behaviour. The public marketing site is unchanged.

* **Control deck.** A shared secondary-navy command rail is the frame in both themes; the workspace is edge-to-edge beside it, with no rounded outer card, dark perimeter or inset gutter consuming page width.
* **Precision workspace.** A cool low-contrast canvas with one continuous topbar. Panels, tables, metrics and action strips use restrained **6/8/12 px** radii, a single hairline where structure needs it, and **no card shadow**.
* **Published cobalt** for authenticated primary actions. **Semantic status colours stay independent so blue never means both brand and health.**
* **Context before controls.** The topbar identifies the current site before global actions; the client current tenant lives in the command-rail Tenants group. Identity and role are visible at desktop widths and collapse to the avatar on smaller screens.
* **Command rail.** Text navigation rows, a quiet active surface, compact group labels, role-aware destinations, and a decorative logo watermark that does not intercept pointer events.
* **Responsive continuity.** Below the desktop breakpoint the workspace stays edge-to-edge and the accessible off-canvas navigation takes over.

Source of truth:

| Layer                                                                                  | File                                                                               |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Console-only tokens and geometry                                                       | `static/css/tokens.css` (under `.l1-console-body`)                                 |
| Shell layout and legacy bridges                                                        | `static/css/shell.css`                                                             |
| Workbench, rail, topbar, shared `l1-*` chrome                                          | `static/css/components.css`                                                        |
| Page primitives (`ui-page`, `ui-panel`, `ui-btn`, `ui-table`, `ui-stat`, `ui-metrics`) | `assets/tailwind.css` → `static/css/tailwind.css`                                  |
| Shared frame markup                                                                    | `templates/layouts/base.html`, `_console_sidebar_top.html`, `_console_topbar.html` |
| Pre-paint literals                                                                     | `templates/layouts/_theme_bootstrap.html`                                          |

**Do not redefine tokens or the shared components elsewhere, and do not add a late override stylesheet.** `app.css` is the remaining legacy layer being folded into `components.css`/`shell.css` as pages migrate; `v2-corporate.css` is already gone and must not come back. `scripts/smoke_all_pages.py` audits rendered `l1-*` classes against the stylesheets each page actually links.

**Layouts.** `templates/layouts/base.html` is the console shell **also used by the authenticated client area**; `public_sidebar.html` is the authenticated public status pages; `marketing.html` is the public site; `auth.html` is sign-in/register; `error.html` is the bare error pages. **Keep navigation and active-state logic in `base.html` and `public_sidebar.html` in sync** — they duplicate the same sidebar. Console pages use `layouts/_console_topbar.html`, **not** the marketing navbar.

**Status colour comes from the `l1_ui` template filters** (`status_tone`, `priority_level`, `severity_level`) — **never an inline `{% if %}` chain**, or the same status ends up green on one page and amber on another. In Tailwind markup use `data-tone="{{ x|status_tone }}"` with `tw:data-[tone=ok]:bg-ok` variants so the mapping stays in `l1_ui.py`. Destructive row actions stay quiet (`.l1-row-action[data-tone="danger"]`) and bulk actions stay hidden until a row is selected.

**Page contract.** Every authenticated content template extends the shared shell and composes `.ui-page` plus `.ui-page__head` (directly or through `_page_header.html`): one page, one head, `.ui-eyebrow`/`.ui-title`/optional `.ui-subtitle`, `.ui-panel` for a bounded data or form region, `.ui-table` or `.ui-row` for records, `.ui-stat`/`.ui-metrics` for figures, status tone from the filters, **one filled primary action per page**, `includes/_empty_state.html` rather than a bare paragraph, and `tw:` utilities for layout. The design-system suite enforces that inventory so a new page cannot silently fall back to legacy container/card markup.

## 12.2 Admin presentation <a href="#id-122-admin-presentation" id="id-122-admin-presentation"></a>

The admin area went through three passes, and the current state is the third:

1. **Task-first** (`l1-admin-workspace`, `data-workspace-kind`): the operator's next task determines the hierarchy. A shared shell does not require every page to contain the same set of cards. **Directories lead with filters and rows; forms do not require a metric summary.** `l1-work-section` is a flat semantic region with a single divider; `l1-work-context` and `l1-work-summary` use ordinary-sized facts, **not oversized KPI cards**; `l1-work-layout` places a quiet context column beside the primary task and stacks on narrow screens.
2. **Brightline**: restored contrast and purposeful colour without bringing back oversized metrics or a card around every section.
3. **Palette and readability**: light mode uses a neutral white `#ffffff` workspace and topbar with stronger `#dce9ff` context regions, blue table headers and quiet alternating rows; **dark mode uses a charcoal scale** (`#151619` canvas, `#191b1f` rail, `#222428` panels, `#25272c` admin context) after the blue dark canvas was rejected. Cobalt remains the action/selection colour, teal section headings add a level **without assigning a new meaning to status colours**, and admin table hover is a quiet neutral surface.

Revenue, Insights and Automatic charges share the palette through `l1-admin-analytics` while keeping their analytical metrics, layout and type scale; task pages use `l1-admin-workspace`. Both markers are scoped to the admin shell and **follow partial navigation** (`.l1-admin-console-body:has(.l1-admin-workspace)` follows the actual page rather than a stale body flag). Client portal, public pages, saved theme preferences and status semantics are unchanged, and **pre-paint literals match the canonical token definitions.**

**Content priorities by area** (what leads, what becomes secondary): a four-box home strip in the WHMCS pattern — Income, Orders, Tickets and To-Do for Super admin; Clients, Services, Tickets and To-Do for Support — then remaining overview counts as compact tiles (failed deployment jobs as a number, not a pending-job tile; no failed-payments card), then a two-column board of tickets, orders, recent clients, signed-in users, incidents and the rest — those lists stay on the page, not inside collapsed details, in catalog order (role filter plus hide empty failed-work). Colour on the four boxes is **category chrome** (income vs tickets vs to-do), not a health status; status chips inside the widgets still come from the `l1_ui` filters, and cobalt stays the action colour. The Income box is the same compact paid-invoice USD snapshot as before (today and this local week, plus new orders on the Orders box — not hourly usage) and is not on Support's catalog. The board **Sales** widget is a full-width WHMCS System Overview graph of those same paid invoices plus new and activated orders, switchable across Today, This Month and This Year; Support does not see it. The board is a two-column CSS grid; shorter panels leave whitespace beside taller neighbours rather than packing into holes. Signed-in client and recording pagers replace only that panel. Identity, holds, services and credit over login IP and repeated counts on Clients; service identity, state, owner and recovery over provisioning history on Services; customer, item, settlement and fulfilment over population counts on Orders; the queue, the editable ticket header, the activity stream and the contact over older history on Tickets (the three-column workbench in [8.1](/platform/support.md#id-81-tickets); `l1-ticket-columns` puts the live editor left, the stream centre and the contact right, and the composer is the floating `l1-ticket-dock`, not a column); failed/running/queued work and safe recovery over scheduler internals on Jobs; the selected configuration form over status cards on Settings — and so on for all 25 areas.

**Important distinctions stay accurate and must not be smoothed away:** missing checks do not prove an outage; configured credentials do not prove readiness; invoice values are not cash receipts; mixed currencies are never summed; accepted node jobs are not running. Warnings, failed work, required fields and validation errors remain visible.

**Navigation cleanup.** The command rail is the home for unfiltered cross-section navigation, so repeated cross-section shortcuts were removed. Breadcrumbs, primary actions, section tabs, pagination and record-specific links stay, and a warning or filtered work queue can still link to the appropriate **recovery** destination. **Navigation removal does not change route authority.**

**Shared directory previews.** Clients, Orders and Invoices share one read-only preview interaction (`admin-directory-preview.js` plus a common include): single-click a name or non-interactive row area to preview, double-click opens the real record link. Other links, bulk checkboxes and action controls retain their own behaviour; native new-tab access, keyboard activation, Close/Escape focus return and no-JavaScript links remain available. The preview is sticky and height-bounded on desktop; narrow layouts open it only on explicit selection. **Preview content is escaped server markup for the current paginated results**, uses page-bounded queries and cache reads, and introduces no new endpoints, models, authorization rules, saved preferences or business mutations. **Selection adds only the blue left-edge marker.** Honesty rules inside previews: current catalog specifications are identified as **current** rather than implied to be historical order snapshots, and invoice face value is not relabelled as account credit or cash received.

**Services directory.** Clicking a row or title opens the service; there is no directory preview pane. Support still lands on Usage for a VPS and the client record for hosting. Opening the directory **never polls a hypervisor or queues work.**

**Job category tabs** are described in [5.1](/platform/vps/provisioning.md#id-51-the-provisioning-job-engine).

## 12.3 Client workspace <a href="#id-123-client-workspace" id="id-123-client-workspace"></a>

Client access mode adds `.l1-client-portal-body`; product pages within it also carry `.l1-client-page` plus one task-family modifier (`--dashboard`, `--hub`, `--detail`, `--form`, `--document`, `--settings`, `--workspace`). **The extra page marker is intentional:** authenticated public status and Looking Glass screens render inside the same shell but are distinct public network tools and must not inherit the client-product treatment.

The client layer uses compact technical-console introductions, records, metric bands and detail lists, with readable forms and **44 px touch controls**. Titles cap at 26 px, section headings are 14 px, table cells use 8 px vertical padding, page gaps are 16 px, desktop section navigation rows are 36 px, and mobile/coarse controls keep 44 px targets.

Sign-in, `/client/`, and `/client/dashboard/` land on **Instances**. `/client/dashboard/` remains as a redirect so old bookmarks still work. Tenure standing — current tier, streak, deposit bonuses and bandwidth milestones — lives on **Rewards program** in Account, above Refer and earn, and reads `rewards_context` so it cannot disagree with the add-credit notice or the bandwidth pool. Members open that page and see the organization's standing; Add credit stays owner-only.

**Section sidebars.** Client page sections use the shared secondary sidebar (`l1-section-workspace` / `l1-section-sidebar` in `components.css`): instance Overview / Usage Graphs / Settings / Network; Billing Statements / Receipts / Bandwidth / Payment methods; Networking Firewall Groups / Private networks / Floating IPs / Reverse DNS / Bandwidth usage; Account settings and Developer tools; Refer & Earn programs; Aegis. Horizontal section bars are gone. **The shell selects a fixed sidebar partial through `apps.core.client_navigation` and renders it inside `#l1-console-content`, so partial navigation replaces both the selected section and the content.** Most section rails stack as vertical navigation on small screens (two columns on phones).

**Instance navigation.** The sidebar groups console and power controls under **Power & access**, with upgrade and reinstall under **Manage instance**, using full-width rows on desktop, and keeps **Destroy at the bottom, below Reinstall**, with its existing confirmation and disabled states. **Below 768 px** the rail moves immediately after the identity/status header and becomes a single row of Overview / Usage / Settings / Network, with Console directly accessible beside a native **Actions** disclosure containing power and management controls (Escape, outside click and moving focus outside dismiss it; dialogs return focus to the visible Actions control). The shared shell initializes this on both full and partial loads and restores the desktop rail when the viewport widens, **moving the original elements so links, POST forms and dialog IDs stay unique.** Operator pages keep their header toolbar.

Instance **Overview** uses compact unboxed Instance and Access definition lists for specifications, network details and credentials, with a fixed label column. Location shows the cluster's location label **without the node name**. **The root password starts as a fixed mask with an eye control**; its native disclosure reveals the value inline and can hide it again, including after partial navigation. **The value sits outside the disclosure's summary so it stays hidden if CSS fails to load**, and remains masked for session recordings; the disclosure fits its contents so the eye stays beside the revealed text, capped to the detail column so long passwords wrap on phones. Scheduled deletion and its **Keep server** action remain visible as a notice when relevant. **Recent actions** is a chronological list with status, actor, time and **customer-safe** errors; related receipts are omitted. VNC guidance and paginated session history live below the console viewer on the authorized console page.

There are **no client preview panels or row-click behaviours**, and this layer introduces no route, model or permission changes.

## 12.4 Tailwind migration <a href="#id-124-tailwind-migration" id="id-124-tailwind-migration"></a>

**Tailwind is mid-migration off Bootstrap; CSS is a build artifact now.** `assets/tailwind.css` is the source, `static/css/tailwind.css` is **generated** — **never hand-edit the generated file.** Rebuild after adding or renaming any utility class, or it gets tree-shaken away and silently does nothing:

```bash
sh scripts/build_css.sh          # or --watch during development
sh scripts/build_css.sh --marketing-only   # after changing marketing CSS sources
```

The Dockerfile runs this before `collectstatic`, using the Tailwind standalone binary (the image has no Node). Gotchas already paid for:

* **Utilities are unlayered on purpose.** Layered ones lost to app.css's broad element resets — `a { color: inherit }` silently killed every text-colour utility on a link.
* **Button-styled links need `tw:no-underline`** until Bootstrap is gone: its `a { text-decoration: underline }` still applies and Tailwind's preflight is deliberately not loaded yet.
* **The prefix goes first, before any variant**: `tw:hover:bg-brand-strong`, `tw:lg:grid-cols-2`, `tw:last:border-b-0`. Writing `hover:tw:bg-…` **compiles silently to nothing** — no error, no rule, the style just never applies.
* **Utilities are prefixed `tw:`** for as long as Bootstrap is loaded. Bootstrap and Tailwind share utility names with **different values** (`mb-3` is 1 rem vs 0.75 rem, likewise `p-`/`px-`/`py-`/`gap-`/`mt-`/`me-`/`ms-`, `border`, `rounded`, `table`, `flex`, `hidden`, `sticky`), and Tailwind cannot tell a Bootstrap class in a template from one of its own — it will happily generate `.mb-3` and resize the whole console. **The prefix comes off only when Bootstrap does.**
* **The `@theme` block maps onto `var(--l1-*)` rather than hex values**, so `tw:bg-surface-1` follows the dark-mode token swap with no `dark:` variants. Hardcoding colours there breaks dark mode.
* **Preflight is deliberately not imported** while Bootstrap's Reboot is on the page. Add it in the same change that removes Bootstrap.
* **`@source` covers templates, `apps/**/*.py` (form widget attrs) and the JS that builds rows at runtime.** A class that only ever appears outside those paths will not be generated.
* **`.tools/` is in both `.gitignore` and `.dockerignore`** — the binary is platform-specific, and shipping a macOS build into the Linux image makes the Docker build fail with an exec format error.

## 12.5 Dark mode and theme preference <a href="#id-125-dark-mode-and-theme-preference" id="id-125-dark-mode-and-theme-preference"></a>

**Dark mode is console-only, and `data-l1-theme-dark-supported` on `<html>` is the whole answer.** The public site, the auth pages and the error pages are light **always**, including for a signed-in account whose preference is dark: those layouts load `_marketing_styles.html` and have no dark token set, so honouring a preference there paints half a page with colours nothing was designed against.

`layouts/base.html` — the authenticated shell (console, client portal, authenticated status pages) — is the **only** layout that says `"true"`. `_theme_bootstrap.html` and `static/website/js/site.js` both read the attribute and treat anything else, **including its absence**, as light only, so a new layout is light until it opts in. **Both bail out rather than merely hiding controls**, or a stale `dark` in `localStorage` (or the OS-change listener) repaints a page with no dark tokens. The dark code is disabled publicly, **not deleted**, so the console side stays in one place — but **a public page must not grow a theme control**, since `site.js` ignores it there and the button would render and do nothing.

**A theme preference is not a theme, and `system` is the default.** `ThemePreference` is `system`/`light`/`dark`; `data-l1-theme` is only ever `light` or `dark`. `data-l1-theme-preference` holds the **preference** and `data-l1-theme` holds the **resolution**.

Only the browser knows the device setting, so **the server cannot resolve `system`**, and three consequences follow:

* The `data-l1-theme` in the `<html>` tag is a **placeholder** (and the correct no-JS fallback); `_theme_bootstrap.html` overwrites it.
* **That resolution must happen in `_theme_bootstrap.html`, the first script in `<head>`, not in the deferred `site.js`.** `system` is the default for every console user, so resolving it after first paint is a flash of light for every dark-device operator — the whole population, not an opted-in minority.
* **`accounts:theme-preference` stores `system` as itself** and never resolves it. Writing back `dark` because this request came from a dark device pins the account to dark and stops it following the device.

Controls: `aria-pressed` on the Appearance buttons tracks the stored **preference**, not what is on screen (on a dark device with System selected the page is dark, and marking Dark as pressed would report a choice the user never made). The console topbar toggle flips to an explicit `light` or `dark`, because a two-state control can only unambiguously mean "give me the other one". **Both are bound by one delegated listener on `document`**, since the console swaps page content in without a reload and listeners bound at load were dead on the Account settings page whenever it arrived by partial navigation.

`site.js` keeps a `matchMedia('(prefers-color-scheme: dark)')` listener and repaints when the preference is `system`; without it, flipping the device to dark leaves an open tab stale until the next full page load — which in the console shell can be a whole session.

Migration `0016_theme_preference_system_default` widened the choices and moved **every existing row** onto `system`, reversing `0006_theme_preference_default_light`. **Do not re-narrow the choices without deciding what happens to the stored `system` rows.**

Deliberately dead: `layouts/error.html` keeps its `html[data-l1-theme="dark"]` palette and `_theme_bootstrap.html` still ships its console pre-paint block on public pages — both inert there because the attribute never says dark, and both kept so the console side stays in one place. The error page's own theme script is gone and its `<meta name="color-scheme">` is `light`.

## 12.6 Navigation, route naming and partial navigation <a href="#id-126-navigation-route-naming-and-partial-navigation" id="id-126-navigation-route-naming-and-partial-navigation"></a>

**One sidebar entry can front several pages, but each tab keeps its own url name.** `staff_access.py` gates on `(app_name, url_name)` of the **resolved** route, so folding five log views behind one url name with a `?tab=` branch would make one allowlist entry stand for five pages, and the next `SUPPORT_CONSOLE_ROUTES` addition would grant four more than it named. The tab strip is therefore a **shared partial that reads the active tab off `request.resolver_match`** (`layouts/_console_log_tabs.html`, `_console_service_tabs.html`, `_console_revenue_tabs.html`, `_console_ipam_tabs.html`, `_console_referral_tabs.html`, …), which also means adding a page is one line in the partial.

`?tab=` with a `TABS` tuple plus a `_tab()` validator (`CLIENT_DETAIL_TABS`, `SETTINGS_TABS`, `CLIENT_BILLING_TABS`) is for the other case: **one view over one record**, where only the active tab's querysets should be built.

**Guard every nullable FK in a `{% url %}`.** A `None` argument raises `NoReverseMatch` and 500s the page, and `scripts/smoke_all_pages.py` cannot catch it because it only renders parameterless URLs, so it never reaches a detail page. Use `{% if obj.thing_id %}` and a plain hyphen for the empty cell.

Console sidebar structure: **Dashboard**, then collapsible Customers, Billing, Support, Operations, Infrastructure, Content, Settings. The client rail is **Tenants** when an organization is selected, then **Manage** and **Account**, each framed by a hairline. Deploy lives in the client top bar via **Create**, not in the rail. Group expansion is remembered in browser-local storage and the current destination's group opens automatically; native `details` elements leave every destination available without JavaScript or working storage. Manage, Account, and admin groups default expanded; Tenants defaults collapsed. Sidebar entries carry `data-l1-nav-prefixes` so a related page lights the right entry, and `data-l1-nav-exact` where a prefix match would light too much (the Revenue entry's href is the billing mount, so a prefix match would light it for every page under `/console/billing/`). Active state reads `current_url` **and** `current_app` where a url name is shared (`admin-dashboard` exists in both `core` and `billing`).

**Global console search** (`core:admin-search`) is GET-only from the topbar (centered between the workspace caption and the account controls), accepts 2–100 characters, and returns one ranked list (at most twenty hits). Lexical candidates cover client email/name/company/tenant, VPS name/hostname/primary IP/VMID/MAC/plan, hosting domain/username, ticket number/subject/requester, incident number/title, invoice number/account, and for a super admin also orders (number, email, plan), provisioning jobs, products, and plans. Matching an account or server also pulls related records (the order behind a VM, the client's other services) so they can be ranked, not so they become a second search. Tickets and incidents use the selected `site`; billing records keep the platform-wide scope of the existing lists.

**Live services outrank the paperwork that created them**: a virtual server sorts above its order, invoice, job, and plan. When the documentation-search model has an API key it reorders those candidates (staff tool: the public assistant switch does not have to be on). A timeout or parse failure keeps the heuristic order. **There is still no suggestion endpoint, message-body lookup or credential lookup**, and search selects display fields only — the model never sees ticket bodies, passwords, job payloads, or payment secrets. **Support never queries orders, jobs, or catalog rows**; VPS results lead to Usage for Support and Overview for a super admin; hosting results lead to the client's Services tab for Support and the hosting detail for a super admin. Browse links under the ranked list open each full directory with the same query and the filters needed to retain closed tickets and ended services.

**Partial navigation script lifecycle.** HTMX replaces `#l1-console-content`, but scripts in that fragment do not provide a reliable initialization lifecycle. Page JavaScript that supports partial navigation belongs in an **inert `template[data-l1-page-assets]` manifest** and must initialize on `l1:content-loaded`; `static/js/app.js` loads and deduplicates those assets before dispatching that event.

For compatibility with older templates, **the shell inspects the incoming content before each swap: if it contains any executable script outside an asset manifest, the shell cancels the swap and performs a normal document navigation.** This makes script-dependent controls work on first entry instead of requiring a manual refresh. Non-executable data scripts such as `application/ld+json` do not trigger the fallback.

Each shell carries `console_shell_context` in `data-l1-console-shell-context`; content requests send it as `X-L1-Console-Shell` and authenticated console/client HTML returns the current identifier in that header. The identifier covers the user, staff role, access mode, **selected tenant** and resolved site — **it is a rendering check, never an authorization credential.** A changed shell context returns `HX-Redirect` so the browser replaces the entire document, which keeps an older tab from retaining its Admin sidebar after another tab selects Client mode, signs in as a different account, changes the staff role or switches tenant. **Mode redirects on content requests also use `HX-Redirect`**: a normal 302 would be followed inside HTMX and leave the old shell mounted. The browser checks the response context before swapping and reloads a persisted back/forward document. **HTMX history snapshots are disabled** so sensitive console content is not stored in browser local storage, and **every authenticated `/console/` and `/client/` response is `no-store`**, including JSON, downloads and streams. Forms, downloads, public pages, site switching, access-mode changes and tenant switches retain full browser navigation (`data-l1-full-navigation`).

Both admin and client partial navigations use the shared `l1-console-nav-loader` spinner overlay; silent provisioning refreshes are loader-free.

The **mobile navigation drawer** moves focus inside when opened, traps Tab and Shift+Tab, closes with Escape and restores focus to its opener; closed mobile navigation is inert and background content is inert while it is open, with ARIA state synchronized across desktop/mobile transitions.

## 12.7 External links <a href="#id-127-external-links" id="id-127-external-links"></a>

**http(s) links that leave layeronecloud.com open in a new tab.** `www` and every subdomain of `layeronecloud.com` count as the same site, as does the current origin (so local development and preview hosts are not rewritten into production). `mailto:`, `tel:`, fragments, relative paths, downloads, HTMX requests, and an explicit `target` other than `_blank` stay in the current tab. Opt out with `data-l1-same-tab`. Console "view public page" links keep their existing `target="_blank"` even though they are first-party.

`rel="noopener noreferrer"` is added when a new tab is opened so the destination cannot script the opener. Existing `rel` tokens such as `me` are kept.

Source of truth: `apps/core/links.py` (`href_is_external`) for generated HTML (chat Markdown, CMS rich text, builder buttons) and `static/website/js/site.js` for every rendered page, including after HTMX swaps. Do not sprinkle `target="_blank"` onto first-party nav.

***


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.layeronecloud.com/platform/design-system.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
